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