I'm trying to add some search on the RecyclerView list without using
notifyDataSetChanged()
instead to it using
diffutil.callback()
but the issue is that it change the list correctly but it doesn't change the UI correctly
Here is my code and I will explain it
class RecordsAdapter : RecyclerView.Adapter<RecordsAdapter.ViewHolder>() {
var adapterList = listOf<CustomerModel>()
var modelList = listOf<CustomerModel>()
set(value) {
adapterList = value
field = value
}
private var modelListFiltered = listOf<CustomerModel>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
ViewHolder(CustomerCellBinding.inflate(LayoutInflater.from(parent.context), parent, false))
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(adapterList[position])
}
override fun getItemCount(): Int = adapterList.size
fun filter(isFiltered: Boolean, filterSearch: String) {
if (isFiltered) {
val filter = modelList
.filter {
it.name.contains(filterSearch) || it.id.contains(filterSearch)
}
modelListFiltered = filter
}
adapterList = if (isFiltered) modelListFiltered else modelList
val diff = CartDiffUtil(
if (isFiltered) modelList else modelListFiltered,
if (isFiltered) modelListFiltered else modelList
)
DiffUtil.calculateDiff(diff).dispatchUpdatesTo(this)
}
inner class ViewHolder(private var binding: CustomerCellBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(model: CustomerModel) {
binding.let {
it.model = model
it.executePendingBindings()
}
}
}
}
class CartDiffUtil(private val oldList: List<CustomerModel>, private val newList: List<CustomerModel>) : DiffUtil.Callback() {
override fun getOldListSize() = oldList.size
override fun getNewListSize() = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
oldList[oldItemPosition].id == newList[newItemPosition].id
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
oldList[oldItemPosition] == newList[newItemPosition]
}
So I'm calling filter function to filter and I'm sending two parameters on if there is any filter and the second is the search.
Now the issue appears in this scenario
0. searching ""
1. searching "testing 2"
2. searching "testing 4"
3. searching "testing 2"
4. searching ""
As you can see in the images, when I search for "testing 2" after "testing 4" it keeps showing "testing 4" and even if I clear the search it gives me two cells of "testing 4" instead of one "testing 2" and one "testing 4"
Hope my question is clear.
Thanks.
CodePudding user response:
I'm guessing your juggling of three list properties is leading to some situations where there can be the same list instance in the before and after of the DiffUtil so it cannot successfully compare them.
Also, it's much easier to use ListAdapter instead of RecyclerView.Adapter when you want to use DiffUtil. Note that when you use ListAdapter, you use ItemCallback instead of Callback. ItemCallback is simpler.
Try doing it this way, where there is only the modelList
and when it or the filter changes, you determine what the new list is and submit it to the ListAdapter and let it handle the changes.
class RecordsAdapter : ListAdapter<CustomerModel, RecordsAdapter.ViewHolder>(CustomerModelCallback) {
var modelList = listOf<CustomerModel>()
set(value) {
field = value
resync()
}
private var filterText: String = ""
private var isFiltered: Boolean = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
ViewHolder(CustomerCellBinding.inflate(LayoutInflater.from(parent.context), parent, false))
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(adapterList[position])
}
fun filter(isFiltered: Boolean, filterText: String = "") {
this.isFiltered = isFiltered
this.filterText = filterText
resync()
}
private fun resync() {
val newList = when {
isFiltered && filterText.isNotEmpty() ->
modelList.filter {
it.name.contains(filterSearch) || it.id.contains(filterSearch)
}
else -> modelList
}
submitList(newList)
}
// view holder...
}
object CustomerModelCallback : DiffUtil.ItemCallback<CustomerModel>() {
override fun areItemsTheSame(oldItem: CustomerModel, newItem: CustomerModel): Boolean =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: CustomerModel, newItem: CustomerModel): Boolean =
oldItem == newItem
}