3

Hi i'm trying to use listadapter and diffcallback in my app. Somehow the view not update at all, when i do something.

When i put some log inside my "areItemsTheSame" and "areContentsTheSame" inside callback, it's not called at all.

Here is my mainActivity

class MainActivity : AppCompatActivity() {

private lateinit var recyclerView: RecyclerView
private lateinit var viewAdapter: TodoAdapter
private lateinit var viewManager: RecyclerView.LayoutManager

private lateinit var viewModel : TodoViewModel

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)

    //viewModel
    viewModel = ViewModelProviders.of(this).get(TodoViewModel::class.java)
    viewManager = LinearLayoutManager(this)
    viewAdapter = TodoAdapter(viewModel.todos)

    binding.btnNew.setOnClickListener {
        viewModel.todos.value!!.add(Todo(3, binding.newText.text.toString()))
    }

    recyclerView = binding.myRecyclerView

    recyclerView.apply {
        layoutManager = viewManager
        adapter = viewAdapter
    }

    viewModel.todos.observe(this, Observer{ list ->
        viewAdapter.submitList(list)
        Log.i("debug", "im @observe " + list.toString())
    })

}

}

and here is my Adapter

 class TodoAdapter(var items: MutableLiveData<ArrayList<Todo>>):
    ListAdapter<Todo, TodoAdapter.MyViewHolder>(TodoDiffCallback()) {

// Create new views (invoked by the layout manager)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
    // create a new view
    val inflater = LayoutInflater.from(parent.context)
    val binding  = ListItemBinding.inflate(inflater)

    return MyViewHolder(binding)
}


override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    holder.todoText.text = getItem(position).task

    //delete
    holder.delBtn.setOnClickListener {
        items.value!!.removeAt(position)
        submitList(items.value)
        Log.i("debug", "@adapter what is items " + items.value.toString())
    }

//override fun getItemCount() = items.value!!.size


class MyViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root) {
    val todoText = binding.todoItem
    val delBtn = binding.btnDelete
    val ediBtn = binding.btnEdit
}

}

class TodoDiffCallback : DiffUtil.ItemCallback<Todo>(){

override fun areItemsTheSame(p0: Todo, p1: Todo): Boolean {
    Log.i("debug", " here " +p0 + " inItems " + p1 )
    return p0 == p1
}

override fun areContentsTheSame(p0: Todo, p1: Todo): Boolean {
    Log.i("debug", "here " + p0 + " inContents" + p1 )
    return p0.equals(p1)
}

}

my ViewModel

class TodoViewModel: ViewModel() {

val todos = MutableLiveData<ArrayList<Todo>>()

init{
    todos.value = arrayListOf(
        Todo(1, "cooking"),
        Todo(2, "washing")
    )
}

}

Unfortunately the android docs didn't give more examples on this.

Again, both "logs" inside my DiffUtil class not called at all. When i click remove button, the UI not updated but the log show the correct results.

thanks

MisterCat
  • 1,531
  • 3
  • 13
  • 23
  • can i see your view model? – Rofie Sagara Oct 04 '19 at 08:35
  • are you sure that your list is indeed changing? – r2rek Oct 04 '19 at 08:36
  • i had same problem with you yesterday. thats because the list u use is same index reference in viewmodel you can try debug and check the list in adapter and in livedata view model my solution but not the best is make copy of array from live data and post new array with difrrent reference – Rofie Sagara Oct 04 '19 at 08:36
  • @RofieSagara i added it in question above, thanks – MisterCat Oct 04 '19 at 08:37
  • you're not really changing the value of todos after they are being initially set. why would the diffutils be triggered? – r2rek Oct 04 '19 at 08:39
  • loadPost(categoryName, locationId, offset) .also { Timber.i("List from server ${it.size}") val newPost = ArrayList() _threadData.value?.forEach { dt -> newPost.add(dt.copy()) } newPost.addAll(it) _threadData.postValue(newPost) } i use like that to make copy list – Rofie Sagara Oct 04 '19 at 08:40
  • @r2rek Hi, 1. the items.value inside my "delete onclick" indeed changes. It shows in log. 2. I'm not really sure, can you point out what should i change – MisterCat Oct 04 '19 at 08:40
  • change your ```submitList(items.value)``` to ```submitList(items.value.toMutableList())``` and let me know if it worked. I can provide proper answer then :) – r2rek Oct 04 '19 at 08:54
  • Please post your submitList() method – Giorgos Neokleous Oct 04 '19 at 09:30
  • @GiorgosNeokleous Hi, i dont write one, its method from listadapter – MisterCat Oct 04 '19 at 09:35
  • @r2rek indeed it works for delete. thanks. I have to add "submitList" also inside "add new item" listener in mainactivity . I though the purpose of observe to make it all run automatically? 2. can you expplain why adding toMutableList() work? what did i do wrong. And currenlty if i delete all items, the app will crash. Thanks for the guidance – MisterCat Oct 04 '19 at 09:37
  • @MisterCat did you see [this question](https://stackoverflow.com/questions/49726385/listadapter-not-updating-item-in-recyclerview). – ysfcyln Oct 04 '19 at 13:13
  • @ysfcyln thanks for the link, i'll try all the solutions provided – MisterCat Oct 04 '19 at 22:43
  • thanks for all your hints and answers , its really helpful! let me know if i can make it better. I wrote my current solution – MisterCat Oct 05 '19 at 01:59

1 Answers1

1

Current working solution

I read about the "observe in liveData", it doesn't triggered until i call "setValue" method.

So i add some new methods in my ViewModel class, where i apply the setValue example on adding newItem

fun addTodo(text: String) {
    _todos.value!!.add(Todo(3, text))
    _todos.setValue(_todos.value)
}

So far it works. I now only run submitList once, in my observe in MainActivity

   viewModel.todos.observe(this, Observer{ list ->
        viewAdapter.submitList(list.toMutableList())
    })

I still have a problem, which the "position" of the items, still somehow not right. Example:

  1. when i remove all items, the app crashes
  2. after i remove item from 2nd position (array[1]) and then i remove the new added task, it will give "error index out of bound "

So the "position" in onBindViewHolder, haven't changed after delete/or do other things [updated] (Problem solved by using: holder.getAdapterPosition())

MisterCat
  • 1,531
  • 3
  • 13
  • 23