Home > front end >  DiffUtil Not working with ListAdpater when view is updating Android Kotlin
DiffUtil Not working with ListAdpater when view is updating Android Kotlin

Time:10-25

Hey I have Reyclerview with DiffUtill using ListAdapter. I added element through submitList function. But when updating the list view is not redrawing the element. Until I used notifyDataSetChanged() or setting adapter again. So what the use case of DiffUtill?. What is the proper way of doing to redraw item when item is updated in list as well as in reyclerview.

MainActivity

class MainActivity : AppCompatActivity() {

    private var list = mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
    private lateinit var binding: ActivityMainBinding
    var i = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        Log.e("List", " $list")
        val intAdapter = IntAdapter()
        binding.recylerview.adapter = intAdapter
        intAdapter.submitList(list)
        binding.button.setOnClickListener {
            list.add(  i)
            intAdapter.submitList(list)
//            binding.recylerview.adapter = intAdapter
//            intAdapter.notifyDataSetChanged()
        }
    }
}

IntAdapter

class IntAdapter : ListAdapter<Int, IntViewHolder>(comparator) {

    companion object {
        private val comparator = object : DiffUtil.ItemCallback<Int>() {
            override fun areItemsTheSame(oldItem: Int, newItem: Int): Boolean {
                return oldItem == newItem
            }

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

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IntViewHolder {
        return IntViewHolder(
            IntLayoutBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
            )
        )
    }

    override fun onBindViewHolder(holder: IntViewHolder, position: Int) {
        holder.bindItem(getItem(position))
    }

}

IntViewHolder

class IntViewHolder(val binding: IntLayoutBinding) : RecyclerView.ViewHolder(binding.root) {

    fun bindItem(item: Int?) {
        binding.intNumber.text = item.toString()
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@ id/recylerview"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@ id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="add"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

int_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@ id/intNumber"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

CodePudding user response:

You need to ensure you pass a different instance of List each time you call submitList. If you pass a List, mutate it, and then pass that same List instance again, DiffUtil is only comparing the same list to itself, so it will assume nothing has changed in the list and won't update anything. It doesn't have some sort of memory of what the List contained back when you first submitted it.

To generalize further, you must not use a mutable List with ListAdapter at all. ListAdapter assumes the list you pass to submitList does not change. If you mutate it, there can be unexpected bugs.

Two ways to resolve this in your code.

  1. Create a read-only copy of the list each time you pass it:

    intAdapter.submitList(list.toList())

  2. Don't use a MutableList at all. Create a new List every time you modify what should be in the List. This is the simpler, less error-prone solution.

class MainActivity : AppCompatActivity() {

    private var list = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
    private lateinit var binding: ActivityMainBinding
    var i = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        Log.e("List", " $list")
        val intAdapter = IntAdapter()
        binding.recylerview.adapter = intAdapter
        intAdapter.submitList(list)
        binding.button.setOnClickListener {
            list  =   i // create a new list from old list contents plus a new item
            intAdapter.submitList(list)
        }
    }
}

Side note: when you have var combined with Mutable____ that should be kind of a red flag. It should be rare that you need two different ways to change something. That is error-prone.

CodePudding user response:

You need to submit a new List, currently you are submitting that same List which has been mutated.

Inside your click listener try something like :

binding.button.setOnClickListener {
    val current = intAdapter.currentList
    val update = current   (current.size   1) // returns a new list with added value
    intAdapter.submitList(update)
}

Example with this logic :

example

  • Related