2

I implemented a ListAdapter with DiffUtil and faced an issue when appending a new list. It overwrites instead of appending to old one. To solve issue i created a new project and populate it with some test data.

Here is my code:

MainActivity

private lateinit var binding: ActivityMainBinding
private val viewModel: ItemViewModel by lazy {
    ItemViewModel()
}
private val adapter: ItemAdapter by lazy {
    ItemAdapter()
}
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)

    viewModel.getItems()
    viewModel.items.observe(this, Observer { items ->
        adapter.submitList(items)
    })
    binding.recyclerView.adapter = adapter
    binding.fab.setOnClickListener {
        viewModel.getItems(9)
    }
}

ItemViewModel

class ItemViewModel: ViewModel() {
    private val repository = FakeRepository()
    private val _items: MutableLiveData<List<Item>> = MutableLiveData()
    val items: LiveData<List<Item>> = _items
    fun getItems(start: Int = 1) {
        viewModelScope.launch {
            val items = repository.getItems(start)
            _items.value = items
            /*val newItems = items.map { it.copy() }
            _items.postValue(newItems)*/
        }
    }
}

ItemAdapter

class ItemAdapter: ListAdapter<Item, ItemAdapter.ViewHolder>(DiffUtilCallback()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(ItemRowBinding.inflate(LayoutInflater.from(parent.context),parent,false))
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(getItem(position))
    }

    class ViewHolder(private val binding: ItemRowBinding): RecyclerView.ViewHolder(binding.root) {
        fun bind(item: Item) {
            binding.apply {
                title.text = item.title
            }
        }
    }
    private class DiffUtilCallback: DiffUtil.ItemCallback<Item>() {
        override fun areItemsTheSame(oldItem: Item, newItem: Item) = oldItem.id == newItem.id

        override fun areContentsTheSame(oldItem: Item, newItem: Item) = oldItem == newItem

    }
}

Item

data class Item(
    val id: Int,
    val title: String,
    val timestamp: String
)

enter image description here

Zain
  • 37,492
  • 7
  • 60
  • 84
nurmat
  • 361
  • 3
  • 11
  • So, you of course test without commenting out the `postValue()` part in the `ViewModel`? – Zain Sep 29 '21 at 06:59
  • Try overriding `equals()` on data class – Vishnu Sep 29 '21 at 07:04
  • @Zain yes. And it didnt work too – nurmat Sep 29 '21 at 07:17
  • ```repository.getItems(start)```, Inside repository are you appending the list? you should append the list. ```val items: LiveData> = _items.``` This live data is observed in ```MainActivity.kt``` and submitting the list through ```adapter.submitList(list)```. It will update the same list in ListAdapter. – Aman Kumar Sep 29 '21 at 15:35
  • @AmanKumar No, i dont. That was my mistake. I was misunderstand. Now i made appending code block – nurmat Oct 16 '21 at 12:00

1 Answers1

0

As per documentation:

Submits a new list to be diffed, and displayed.

If a list is already being displayed, a diff will be computed on a background thread, which will dispatch Adapter.notifyItem events on the main thread.

So, when you submit a new list via the LiveData observer, it's a brand new list to the adapter, and therefore it overwrites the current items not appending them.

If you want to append the current items, you can create a method in the adapter to consolidate the current list with the new one, and eventually submit it:

class ItemAdapter : ListAdapter<Item, ItemAdapter.ViewHolder>(DiffUtilCallback()) {

//......

    fun appendList(list: List<Item>) {
        val currentList = currentList.toMutableList() // get the current adapter list as a mutated list
        currentList.addAll(list)
        submitList(currentList)
    }

}

And apply that to the observer callback in the activity:

viewModel.items.observe(this, Observer { items ->
      //  myAdapter.submitList(items) // send a brand new list
      myAdapter.appendList(items) // Update the current list
})
Zain
  • 37,492
  • 7
  • 60
  • 84