ListAdapter with DiffUtil and LiveData overwriting items not appending

1.1k Views Asked by At

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

1

There are 1 best solutions below

0
On BEST ANSWER

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
})