📜  如何使用DiffUtil提高Android中的RecyclerView性能?

📅  最后修改于: 2021-05-13 14:56:05             🧑  作者: Mango

DiffUtil类用于提高RecyclerView的性能。 DiffUtil背后的算法是,它接受两个列表并找出两者之间的差异,并将更新后的列表作为输出提供。它使用Eugene W. Myers的差分算法来计算最小更新数。在本文中,我们将看到其实现。请注意,我们将使用Kotlin语言实施此项目。

分步实施

步骤1:创建一个新项目

要在Android Studio中创建新项目,请参阅如何在Android Studio中创建/启动新项目。请注意,选择Kotlin作为编程语言。

步骤2:添加视图绑定依赖项

转到android标签内的build.gradle(app)和以下依赖项,然后单击立即同步。

步骤3:建立新的模型类别

创建一个新的Language.kt类,我们将使用自定义通用“ Language ”的数据来传递将在RecyclerView中显示的列表。

Kotlin
// this is the Language model class
class Language(
    val id : Int =0,
    val name : String ="",
    val exp : String =""
)


XML


  
    
          
        
        
          
        
        
  
        
        
  
    
  


XML


  
    
    
  
    
    


Kotlin
import androidx.recyclerview.widget.DiffUtil
  
// pass two list one oldList and second newList
class MyDiffUtil(
    private val oldList : List,
    private val newList : List
) :DiffUtil.Callback() {
    // implement methods
    override fun getOldListSize(): Int {
        // return oldList size
        return oldList.size
    }
  
    override fun getNewListSize(): Int {
        // return newList size
        return newList.size
    }
  
    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        // compare items based on their unique id
        return oldList[oldItemPosition].id == newList[newItemPosition].id
    }
  
    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        // in here compare each item if they are same or different
        // return false if any data is same else return true
        return when{
            oldList[oldItemPosition].id != newList[newItemPosition].id -> false
            oldList[oldItemPosition].name != newList[newItemPosition].name -> false
            oldList[oldItemPosition].exp != newList[newItemPosition].exp -> false
            else -> true
        }
    }
}


Kotlin
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.geeksforgeeks.rvadapterviewbinding.databinding.SingleItemBinding
  
class RvAdapter() : RecyclerView.Adapter() {
  
    private var oldLanguageList= emptyList()
    // create an inner class with name ViewHolder
    // It takes a view argument, in which pass the generated class of single_item.xml
    // ie SingleItemBinding and in the RecyclerView.ViewHolder(binding.root) pass it like this
    inner class ViewHolder(val binding: SingleItemBinding) : RecyclerView.ViewHolder(binding.root)
  
    // inside the onCreateViewHolder inflate the view of SingleItemBinding
    // and return new ViewHolder object containing this layout
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = SingleItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding)
    }
  
    // bind the items with each item of the list languageList which than will be
    // shown in recycler view
    // to keep it simple we are not setting any image data to view
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        with(holder){
            with(oldLanguageList[position]){
                binding.tvLangName.text = this.name
                binding.tvExp.text = this.exp
            }
        }
    }
  
    // return the size of languageList
    override fun getItemCount(): Int {
        return oldLanguageList.size
    }
  
    fun setData(newLanguageList : List){
        val diffUtil = MyDiffUtil(oldLanguageList , newLanguageList)
        // it calculates the different items of the oldLanguageList and newLanguageList
        val diffResult = DiffUtil.calculateDiff(diffUtil)
        // assign oldLanguageList to newLanguageList
        oldLanguageList = newLanguageList
        diffResult.dispatchUpdatesTo(this)
    }
}


Kotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.geeksforgeeks.rvadapterviewbinding.databinding.ActivityMainBinding
  
class MainActivity : AppCompatActivity() {
  
    // view binding for the activity
    private var _binding : ActivityMainBinding? = null
    private val binding get() = _binding!!
  
    // get reference to the adapter class
    private val rvAdapter by lazy { RvAdapter() }
  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        _binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
  
        // define layout manager for the Recycler view
        binding.rvList.layoutManager = LinearLayoutManager(this)
          
        // attach adapter to the recycler view
        binding.rvList.adapter = rvAdapter
  
        // create new objects
        val language1= Language(1,"Java" , "Exp : 3 years")
        val language2=Language(2,"Kotlin" , "Exp : 2 years")
        val language3=Language(3,"Python" , "Exp : 4 years")
  
        // call set data method of adapter class and the list data
        rvAdapter.setData(listOf(language1, language2, language3))
        binding.btnNewLanguage.setOnClickListener {
            // on click of button add one more item to the list
            val language4= Language(4,"CPP" , "Exp : 5 years")
            rvAdapter.setData(listOf(language1,language2,language3,language4))
        }
    }
  
    // on destroy of view make the binding reference to null
    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }
}


步骤4:创建一个新的布局文件并将其命名为single_item.xml文件

转到single_item.xml文件,并参考以下代码。以下是single_item.xml文件的代码。这是我们将在RecyclerView中使用的单个项目布局。

XML格式



  
    
          
        
        
          
        
        
  
        
        
  
    
  

步骤5:使用activity_main.xml

转到activity_main.xml文件,并参考以下代码。以下是activity_main.xml文件的代码。它只有一个RecyclerView,我们将使用它来显示数据。

XML格式



  
    
    
  
    
    

步骤6:创建一个新类,并将其命名为MyDiffUtil.kt

转到MyDiffUtil.kt文件并编写以下代码。添加了注释以更好地理解代码。

科特林

import androidx.recyclerview.widget.DiffUtil
  
// pass two list one oldList and second newList
class MyDiffUtil(
    private val oldList : List,
    private val newList : List
) :DiffUtil.Callback() {
    // implement methods
    override fun getOldListSize(): Int {
        // return oldList size
        return oldList.size
    }
  
    override fun getNewListSize(): Int {
        // return newList size
        return newList.size
    }
  
    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        // compare items based on their unique id
        return oldList[oldItemPosition].id == newList[newItemPosition].id
    }
  
    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        // in here compare each item if they are same or different
        // return false if any data is same else return true
        return when{
            oldList[oldItemPosition].id != newList[newItemPosition].id -> false
            oldList[oldItemPosition].name != newList[newItemPosition].name -> false
            oldList[oldItemPosition].exp != newList[newItemPosition].exp -> false
            else -> true
        }
    }
}

步骤7:使用Adapter类

创建一个新的类RvAdapter.kt,它将充当回收者视图的Adapter类。使用视图绑定,我们使用布局single_item.xml的生成类(即SingleItemBinding)在我们的案例中在MainActivity.kt的回收者视图中添加数据和视图。为了更好的理解,在代码之前添加了注释。

科特林

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.geeksforgeeks.rvadapterviewbinding.databinding.SingleItemBinding
  
class RvAdapter() : RecyclerView.Adapter() {
  
    private var oldLanguageList= emptyList()
    // create an inner class with name ViewHolder
    // It takes a view argument, in which pass the generated class of single_item.xml
    // ie SingleItemBinding and in the RecyclerView.ViewHolder(binding.root) pass it like this
    inner class ViewHolder(val binding: SingleItemBinding) : RecyclerView.ViewHolder(binding.root)
  
    // inside the onCreateViewHolder inflate the view of SingleItemBinding
    // and return new ViewHolder object containing this layout
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = SingleItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding)
    }
  
    // bind the items with each item of the list languageList which than will be
    // shown in recycler view
    // to keep it simple we are not setting any image data to view
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        with(holder){
            with(oldLanguageList[position]){
                binding.tvLangName.text = this.name
                binding.tvExp.text = this.exp
            }
        }
    }
  
    // return the size of languageList
    override fun getItemCount(): Int {
        return oldLanguageList.size
    }
  
    fun setData(newLanguageList : List){
        val diffUtil = MyDiffUtil(oldLanguageList , newLanguageList)
        // it calculates the different items of the oldLanguageList and newLanguageList
        val diffResult = DiffUtil.calculateDiff(diffUtil)
        // assign oldLanguageList to newLanguageList
        oldLanguageList = newLanguageList
        diffResult.dispatchUpdatesTo(this)
    }
}

步骤8:使用MainActivity.kt文件

转到MainActivity.kt文件,并参考以下代码。下面是MainActivity.kt文件的代码。在代码内部添加了注释,以更详细地了解代码。

科特林

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.geeksforgeeks.rvadapterviewbinding.databinding.ActivityMainBinding
  
class MainActivity : AppCompatActivity() {
  
    // view binding for the activity
    private var _binding : ActivityMainBinding? = null
    private val binding get() = _binding!!
  
    // get reference to the adapter class
    private val rvAdapter by lazy { RvAdapter() }
  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        _binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
  
        // define layout manager for the Recycler view
        binding.rvList.layoutManager = LinearLayoutManager(this)
          
        // attach adapter to the recycler view
        binding.rvList.adapter = rvAdapter
  
        // create new objects
        val language1= Language(1,"Java" , "Exp : 3 years")
        val language2=Language(2,"Kotlin" , "Exp : 2 years")
        val language3=Language(3,"Python" , "Exp : 4 years")
  
        // call set data method of adapter class and the list data
        rvAdapter.setData(listOf(language1, language2, language3))
        binding.btnNewLanguage.setOnClickListener {
            // on click of button add one more item to the list
            val language4= Language(4,"CPP" , "Exp : 5 years")
            rvAdapter.setData(listOf(language1,language2,language3,language4))
        }
    }
  
    // on destroy of view make the binding reference to null
    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }
}

输出:

想要一个节奏更快,更具竞争性的环境来学习Android的基础知识吗?
单击此处前往由我们的专家精心策划的指南,以使您立即做好行业准备!