List Adapter Diff Util not updating the List Item in Recyclerview
Asked Answered
M

2

8

List Adapter diffutil not updating the list item in recyclerview.

It works when I add or remove a item from the list. But not updating the any particular value like isSelected : Boolean = false in object class

How to update the recyclerview view when one of the object class in list is changed like value Boolean changes in one object class

Thankyou

Adapter Class Below

class CategoryMainAdapter(
    var itemClick : CategoryItemClick,
    var screenWidth : Int = 0
) : ListAdapter<CategoryModel, CategoryMainAdapter.CategoryMainViewHolder>(CategoryMainDiffUtils()) {

    inner class CategoryMainViewHolder(val binding : CategorySingleListLayoutBinding) : RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryMainViewHolder {
        val binding = CategorySingleListLayoutBinding.inflate(LayoutInflater.from
            (parent.context),
            parent,
            false
        )
        return CategoryMainViewHolder(binding)
    }

    override fun onBindViewHolder(holder: CategoryMainViewHolder, position: Int) {
        val model = getItem(position)
        holder.apply {
            binding.apply {
                mainLinearLayout.layoutParams.apply {
                    width = ((screenWidth*22.5).toInt())/100
                    height = (((screenWidth*22.5)/100)*1.2).toInt()
                }
                categoryName.text = model.name
                backgroundColor.setBackgroundColor(model.colorCode)
                categoryImage.load(getImageCorrectImage(model.image, model.secondaryImage)) {
                    error(R.drawable.ic_food_place_holder)
                }

                if (model.isSelected) {
                    categoryName.setTextColor(ContextCompat.getColor(categoryName.context, R.color.main_blue))
                    selectedView.setBackgroundResource(R.drawable.main_blue_curved_bg)
                    categoryImage.apply {
                        animation = AnimationUtils.loadAnimation(context, R.anim.category_zoom_in_anim)
                        setPadding(0, 0, 0, 0)
                    }
                } else {
                    categoryName.setTextColor(setColorByAttrProgrammatically(categoryName.context, R.attr.colorOnSecondary))
                    selectedView.setBackgroundResource(android.R.color.transparent)
                    categoryImage.apply {
                        animation = AnimationUtils.loadAnimation(context, R.anim.category_zoom_out_anim)
                        setPadding(1, 1, 1, 1)
                    }
                }
            }
            itemView.apply {
                setOnClickListener {
                    itemClick.onCategoryClick(model)
                }
            }
        }
    }


    class CategoryMainDiffUtils : DiffUtil.ItemCallback<CategoryModel>() {
        override fun areItemsTheSame(oldItem: CategoryModel, newItem: CategoryModel): Boolean {
            return oldItem.isSelected == newItem.isSelected
        }

        override fun areContentsTheSame(oldItem: CategoryModel, newItem: CategoryModel): Boolean {
            return oldItem == newItem
        }

    }

    override fun submitList(list: MutableList<CategoryModel>?) {
        super.submitList(list?.let { ArrayList(it) })
    }
}

Mastic answered 31/1, 2022 at 3:50 Comment(2)
Add your adapter class with question . Add minimal code only if its too big .Red
Hi ADM, I added my adapter code in my questionMastic
E
0

First of, the implementation of areContentsTheSame and areItemsTheSame probably needs to be switched. areContentsTheSame is the one that checks if things in your items are different. areItemsTheSame is about whether it is the same item. If the items have an id you might want to compare them instead of the item themselves. But that alone probably won't fix your issue.

The thing is, ListAdapter works best if the list has items that are immutable. Ideally you want to submit a new list every time you call submitList. Because if you change a property like isSelected in the existing lisk and simply call submitList with that same list nothing will happen.

So what you want to do is instead of changing the isSelected property you need to create a copy of the list and in that copy alone you change the isSelected and pass the copy to submitList.

This can be tedious to make. An alternative ugly workaround that might work is to add a second property maybe called oldIsSelected and only change that one in the DiffUtil itself, like this

    override fun areContentsTheSame(oldItem: CategoryModel, newItem: CategoryModel): Boolean {
        val result = oldItem.oldIsSelected == newItem.isSelected
        newItem.oldIsSelected = newItem.isSelected
        return result
    }
Elana answered 31/1, 2022 at 10:13 Comment(2)
Your both answer doesn't work for me. Below is my copy the list method ArrayList(categoryList.map { it.copy() }).toMutableList().let { list -> list.forEach { resultCategoryModel -> resultCategoryModel.isSelected = false if (resultCategoryModel.id == categoryModel.id) { resultCategoryModel.isSelected = true } } submitList(categoryList) } Mastic
This is the tedious method val result = oldItem.isSelected == newItem.isSelected newItem.isSelected = newItem.isSelected return result Both of the method not work for me.... @Elana BeckerMastic
D
0

Try update your DiffUtil to something like:

class CategoryMainDiffUtils : DiffUtil.ItemCallback<CategoryModel>() {
    override fun areItemsTheSame(oldItem: CategoryModel, newItem: CategoryModel): Boolean {
        return oldItem.id == newItem.id // or compare something unique
    }

    override fun areContentsTheSame(oldItem: CategoryModel, newItem: CategoryModel): Boolean {
        return oldItem == newItem // or if data class is large, compare only things that might change, also please note that only data class on kotlin has auto generated equals() method
    }

}
Deadbeat answered 31/1, 2022 at 10:8 Comment(0)
E
0

First of, the implementation of areContentsTheSame and areItemsTheSame probably needs to be switched. areContentsTheSame is the one that checks if things in your items are different. areItemsTheSame is about whether it is the same item. If the items have an id you might want to compare them instead of the item themselves. But that alone probably won't fix your issue.

The thing is, ListAdapter works best if the list has items that are immutable. Ideally you want to submit a new list every time you call submitList. Because if you change a property like isSelected in the existing lisk and simply call submitList with that same list nothing will happen.

So what you want to do is instead of changing the isSelected property you need to create a copy of the list and in that copy alone you change the isSelected and pass the copy to submitList.

This can be tedious to make. An alternative ugly workaround that might work is to add a second property maybe called oldIsSelected and only change that one in the DiffUtil itself, like this

    override fun areContentsTheSame(oldItem: CategoryModel, newItem: CategoryModel): Boolean {
        val result = oldItem.oldIsSelected == newItem.isSelected
        newItem.oldIsSelected = newItem.isSelected
        return result
    }
Elana answered 31/1, 2022 at 10:13 Comment(2)
Your both answer doesn't work for me. Below is my copy the list method ArrayList(categoryList.map { it.copy() }).toMutableList().let { list -> list.forEach { resultCategoryModel -> resultCategoryModel.isSelected = false if (resultCategoryModel.id == categoryModel.id) { resultCategoryModel.isSelected = true } } submitList(categoryList) } Mastic
This is the tedious method val result = oldItem.isSelected == newItem.isSelected newItem.isSelected = newItem.isSelected return result Both of the method not work for me.... @Elana BeckerMastic

© 2022 - 2024 — McMap. All rights reserved.