Faced such a problem: I have a RecyclerView
, the data for which I get from the ViewModel
using StateFlow
:
viewModel.items.collect {
setRecyclerView(items)
}
Then, let's say, somewhere inside the Fragment
, I change the data for items and there are more of them. In order for my RecyclerView
to see my changes, I have to call the setRecyclerView(items)
function again, which, it seems to me, can lead to the most unexpected consequences (for example, items will be duplicated). The question is: is it possible to somehow change the data and update the RecyclerView
(including the onBindViewHolder
function in it) without yet another creation of an Adapter
?
CodePudding user response:
Let's start talking about the adapter implementation. Reading your question, I believe you used RecyclerView.Adapter to implement your adapter. But there is another option that is simpler and more performant than this. It's the ListAdapter:
The most interesting thing about ListAdapter is the DiffUtil, that have a performative way to check if any item on your list was updated, deleted, or included. Here's a sample of the implementation:
abstract class MyAdapter: ListAdapter<ItemModel, MyAdapter.MyViewHolder>(DIFF_CALLBACK) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding = ItemSimplePosterBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(getItem(position))
}
class MyViewHolder(
private val binding: ItemSimplePosterBinding
): RecyclerView.ViewHolder(binding.root) {
fun bind(item: ItemModel) {
// Here you can get the item values to put these values on your view
}
}
companion object {
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<ItemModel>() {
override fun areItemsTheSame(oldItem: ItemModel, newItem: ItemModel): Boolean {
// need a unique identifier to have sure they are the same item. could be a comparison of ids. In this case, that is just a list of strings just compares like this below
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: ItemModel, newItem: ItemModel): Boolean {
// compare the objects
return oldItem == newItem
}
}
}
}
So, when your list is updated, you just have to call the submitList from the adapter, like this:
viewModel.items.collectLatest { items ->
// You will send the items to your adapter here
adapter.submitList(items)
}
Then, your RecyclerView just has to be configured on onViewCreated for example, and your list can be defined and updated in another place, observing the items change from ViewModel.