📜  使用 Kotlin 在 Android 中扩展 RecyclerView

📅  最后修改于: 2022-05-13 01:54:24.245000             🧑  作者: Mango

使用 Kotlin 在 Android 中扩展 RecyclerView

在本文中,我们将了解如何实现可扩展的 RecycleView。一般来说,我们会在列表视图中显示项目列表,但在某些情况下,如果您想显示子列表项目,那么我们必须使用可扩展列表。请参阅下图以获得更好的理解。

Android中的可扩展RecyclerView

可扩展的回收视图

分步实施

1.如果您观察上图,我们有两种类型的列表项——一种是父项和子项。但是您可以将类似对象的列表传递给回收站视图。所以,我们必须为(父母和孩子)创建一个共同的对象/模型

2. 创建通用模型类

Kotlin
data class ParentData(
    val parentTitle:String?=null,
    var type:Int = Constants.PARENT,
    var subList : MutableList = ArrayList(),
    var isExpanded:Boolean = false
)


Kotlin
object Constants {
    const val PARENT = 0
    const val CHILD = 1
}


Kotlin
data class ChildData(val childTitle:String)


Kotlin
val listData : MutableList = ArrayList()


Kotlin
val parentData: Array = arrayOf("Andhra Pradesh", "Telangana", "Karnataka", "TamilNadu")
val childDataData1: MutableList = mutableListOf(ChildData("Anathapur"),ChildData("Chittoor"),ChildData("Nellore"),ChildData("Guntur"))
val childDataData2: MutableList = mutableListOf(ChildData("Rajanna Sircilla"), ChildData("Karimnagar"), ChildData("Siddipet"))
val childDataData3: MutableList = mutableListOf(ChildData("Chennai"), ChildData("Erode"))


Kotlin
val parentObj1 = ParentData(parentTitle = parentData[0], subList = childDataData1)
val parentObj2 = ParentData(parentTitle = parentData[1], subList = childDataData2)
val parentObj3 = ParentData(parentTitle = parentData[2])
val parentObj4 = ParentData(parentTitle = parentData[1], subList = childDataData3)


Kotlin
listData.add(parentObj1)
listData.add(parentObj2)
listData.add(parentObj3)
listData.add(parentObj4)


Kotlin
class RecycleAdapter(var mContext: Context, val list: MutableList) : RecyclerView.Adapter() {
  
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
  
        return if(viewType== Constants.PARENT){
            val rowView: View = LayoutInflater.from(parent.context).inflate(R.layout.parent_row, parent,false)
           GroupViewHolder(rowView)
        } else {
            val rowView: View = LayoutInflater.from(parent.context).inflate(R.layout.child_row, parent,false)
           ChildViewHolder(rowView)
        }
    }
  
    override fun getItemCount(): Int = list.size
  
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
  
        val dataList = list[position]
        if (dataList.type == Constants.PARENT) {
            holder as GroupViewHolder
            holder.apply {
                parentTV?.text = dataList.parentTitle
                downIV?.setOnClickListener {
                    expandOrCollapseParentItem(dataList,position)
                }
            }
        } else {
            holder as ChildViewHolder
  
            holder.apply {
                val singleService = dataList.subList.first()
                childTV?.text =singleService.childTitle
            }
        }
    }
    private fun expandOrCollapseParentItem(singleBoarding: ParentData,position: Int) {
  
        if (singleBoarding.isExpanded) {
            collapseParentRow(position)
        } else {
            expandParentRow(position)
        }
    }
  
    private fun expandParentRow(position: Int){
        val currentBoardingRow = list[position]
        val services = currentBoardingRow.subList
        currentBoardingRow.isExpanded = true
        var nextPosition = position
        if(currentBoardingRow.type==Constants.PARENT){
  
            services.forEach { service ->
                val parentModel =  ParentData()
                parentModel.type = Constants.CHILD
                val subList : ArrayList = ArrayList()
                subList.add(service)
                parentModel.subList=subList
                list.add(++nextPosition,parentModel)
            }
            notifyDataSetChanged()
        }
    }
  
    private fun collapseParentRow(position: Int){
        val currentBoardingRow = list[position]
        val services = currentBoardingRow.subList
        list[position].isExpanded = false
        if(list[position].type==Constants.PARENT){
            services.forEach { _ ->
                list.removeAt(position + 1)
            }
            notifyDataSetChanged()
        }
    }
  
    override fun getItemViewType(position: Int): Int = list[position].type
  
    override fun getItemId(position: Int): Long {
        return position.toLong()
    }
  
    class GroupViewHolder(row: View) : RecyclerView.ViewHolder(row) {
        val parentTV = row.findViewById(R.id.parent_Title) as TextView?
        val downIV  = row.findViewById(R.id.down_iv) as ImageView?
    }
    class ChildViewHolder(row: View) : RecyclerView.ViewHolder(row) {
        val childTV = row.findViewById(R.id.child_Title) as TextView?
  
    }
}


Kotlin
override fun getItemViewType(position: Int): Int = list[position].type


Kotlin
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return   if(viewType== Constants.PARENT){
            val rowView: View = LayoutInflater.from(parent.context).inflate(R.layout.parent_row, parent,false)
           GroupViewHolder(rowView)
        } else{
            val rowView: View = LayoutInflater.from(parent.context).inflate(R.layout.child_row, parent,false)
           ChildViewHolder(rowView)
        }
    }


XML


  
    
  
    
  


XML


  
    
  
  


Kotlin
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
 
       val dataList = list[position]
       if (dataList.type == Constants.PARENT) {
           holder as GroupViewHolder
           holder.apply {
               parentTV?.text = dataList.parentTitle
               downIV?.setOnClickListener {
                   expandOrCollapseParentItem(dataList,position)
               }
           }
       } else {
           holder as ChildViewHolder
 
           holder.apply {
               val singleService = dataList.subList.first()
               childTV?.text =singleService.childTitle
           }
       }
   }


Kotlin
private fun expandOrCollapseParentItem(singleBoarding: ParentData,position: Int) {
        if (singleBoarding.isExpanded) {
            collapseParentRow(position)
        } else {
            expandParentRow(position)
        }
    }


Kotlin
private fun expandParentRow(position: Int){
       val currentBoardingRow = list[position]
       val services = currentBoardingRow.subList
       currentBoardingRow.isExpanded = true
       var nextPosition = position
       if(currentBoardingRow.type==Constants.PARENT){
 
           services.forEach { service ->
               val parentModel =  ParentData()
               parentModel.type = Constants.CHILD
               val subList : ArrayList = ArrayList()
               subList.add(service)
               parentModel.subList=subList
               list.add(++nextPosition,parentModel)
           }
           notifyDataSetChanged()
       }
   }


Kotlin
list.add(++nextPosition,parentModel)


Kotlin
val exRecycleView = findViewById(R.id.exRecycle)
        exRecycleView.layoutManager = LinearLayoutManager(this)
        exRecycleView.adapter = RecycleAdapter(this@MainActivity,listData)


XML


  
  
    
  


这是我们用于父母和孩子的模型。在数据类类型中 - 默认情况下是父级,isExpanded - 默认情况下是 false 和空子列表。在这里,我们使用文件中的常量,如下所示。

科特林

object Constants {
    const val PARENT = 0
    const val CHILD = 1
}

3. 创建子数据类

在这一步中,我们必须为子项再创建一个数据类,如下所示。

科特林

data class ChildData(val childTitle:String)

4.设置一些数据显示在列表中

现在,我们将向列表中添加一些数据,这些数据将传递给适配器,如下所示

科特林

val listData : MutableList = ArrayList()

这里我们使用 listData,它用于存储 ParentData 对象。现在我们创建父对象和子对象的示例数据,如下所示。

科特林

val parentData: Array = arrayOf("Andhra Pradesh", "Telangana", "Karnataka", "TamilNadu")
val childDataData1: MutableList = mutableListOf(ChildData("Anathapur"),ChildData("Chittoor"),ChildData("Nellore"),ChildData("Guntur"))
val childDataData2: MutableList = mutableListOf(ChildData("Rajanna Sircilla"), ChildData("Karimnagar"), ChildData("Siddipet"))
val childDataData3: MutableList = mutableListOf(ChildData("Chennai"), ChildData("Erode"))

现在是将这些数据转换为 ParentData 对象的时候了,因为我们将把它添加到 ListData。

科特林

val parentObj1 = ParentData(parentTitle = parentData[0], subList = childDataData1)
val parentObj2 = ParentData(parentTitle = parentData[1], subList = childDataData2)
val parentObj3 = ParentData(parentTitle = parentData[2])
val parentObj4 = ParentData(parentTitle = parentData[1], subList = childDataData3)

默认情况下,在 ParentData 模型中,类型为父级,可扩展为 false。现在我们正在添加标题和子列表。最后,我们需要将所有这些对象添加到 listData 中,如下所示

科特林

listData.add(parentObj1)
listData.add(parentObj2)
listData.add(parentObj3)
listData.add(parentObj4)

5. 为回收视图创建适配器

我们刚刚添加了一些虚拟数据,现在我们必须将这些数据显示到回收器视图中,因为我们需要一个适配器,用于根据我们的要求显示我们的数据。

科特林

class RecycleAdapter(var mContext: Context, val list: MutableList) : RecyclerView.Adapter() {
  
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
  
        return if(viewType== Constants.PARENT){
            val rowView: View = LayoutInflater.from(parent.context).inflate(R.layout.parent_row, parent,false)
           GroupViewHolder(rowView)
        } else {
            val rowView: View = LayoutInflater.from(parent.context).inflate(R.layout.child_row, parent,false)
           ChildViewHolder(rowView)
        }
    }
  
    override fun getItemCount(): Int = list.size
  
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
  
        val dataList = list[position]
        if (dataList.type == Constants.PARENT) {
            holder as GroupViewHolder
            holder.apply {
                parentTV?.text = dataList.parentTitle
                downIV?.setOnClickListener {
                    expandOrCollapseParentItem(dataList,position)
                }
            }
        } else {
            holder as ChildViewHolder
  
            holder.apply {
                val singleService = dataList.subList.first()
                childTV?.text =singleService.childTitle
            }
        }
    }
    private fun expandOrCollapseParentItem(singleBoarding: ParentData,position: Int) {
  
        if (singleBoarding.isExpanded) {
            collapseParentRow(position)
        } else {
            expandParentRow(position)
        }
    }
  
    private fun expandParentRow(position: Int){
        val currentBoardingRow = list[position]
        val services = currentBoardingRow.subList
        currentBoardingRow.isExpanded = true
        var nextPosition = position
        if(currentBoardingRow.type==Constants.PARENT){
  
            services.forEach { service ->
                val parentModel =  ParentData()
                parentModel.type = Constants.CHILD
                val subList : ArrayList = ArrayList()
                subList.add(service)
                parentModel.subList=subList
                list.add(++nextPosition,parentModel)
            }
            notifyDataSetChanged()
        }
    }
  
    private fun collapseParentRow(position: Int){
        val currentBoardingRow = list[position]
        val services = currentBoardingRow.subList
        list[position].isExpanded = false
        if(list[position].type==Constants.PARENT){
            services.forEach { _ ->
                list.removeAt(position + 1)
            }
            notifyDataSetChanged()
        }
    }
  
    override fun getItemViewType(position: Int): Int = list[position].type
  
    override fun getItemId(position: Int): Long {
        return position.toLong()
    }
  
    class GroupViewHolder(row: View) : RecyclerView.ViewHolder(row) {
        val parentTV = row.findViewById(R.id.parent_Title) as TextView?
        val downIV  = row.findViewById(R.id.down_iv) as ImageView?
    }
    class ChildViewHolder(row: View) : RecyclerView.ViewHolder(row) {
        val childTV = row.findViewById(R.id.child_Title) as TextView?
  
    }
}

6.在Adapter类中添加Logic

在这一步中,我们将讨论适配器如何显示我们的数据。

科特林

override fun getItemViewType(position: Int): Int = list[position].type

此行用于设置视图类型,默认情况下 ParentData 对象类型是父项,因此它显示父项列表。但是为什么我们在这里添加这种类型。当您将数据绑定到视图时,要通过适配器方法了解哪种类型的视图类型,如下所示

科特林

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return   if(viewType== Constants.PARENT){
            val rowView: View = LayoutInflater.from(parent.context).inflate(R.layout.parent_row, parent,false)
           GroupViewHolder(rowView)
        } else{
            val rowView: View = LayoutInflater.from(parent.context).inflate(R.layout.child_row, parent,false)
           ChildViewHolder(rowView)
        }
    }

这个方法自带viewType,之前已经设置好了。现在我们有机会检查即将到来的类型。如果它是父母,那么我们可以膨胀(将布局转换为视图) parent_row 或 child_row 并返回 GroupViewHolder(rowView) 或 ChildViewHolder(rowView)

父行.xml

XML



  
    
  
    
  

child_row.xml

XML



  
    
  
  

7.如何将数据绑定到视图

科特林

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
 
       val dataList = list[position]
       if (dataList.type == Constants.PARENT) {
           holder as GroupViewHolder
           holder.apply {
               parentTV?.text = dataList.parentTitle
               downIV?.setOnClickListener {
                   expandOrCollapseParentItem(dataList,position)
               }
           }
       } else {
           holder as ChildViewHolder
 
           holder.apply {
               val singleService = dataList.subList.first()
               childTV?.text =singleService.childTitle
           }
       }
   }

这种方法带有一个支架。现在我们必须使用类型来检查它是否是父类型,然后我们可以使用 as(instance of in Java) 关键字将数据视图(如持有者)绑定为 GroupViewHolder。

8. 展开/折叠逻辑的工作原理

当用户单击向下箭头图像时,我们调用 expandOrCollapseParentItem函数。此函数检查父级是否已扩展,如下所示。

科特林

private fun expandOrCollapseParentItem(singleBoarding: ParentData,position: Int) {
        if (singleBoarding.isExpanded) {
            collapseParentRow(position)
        } else {
            expandParentRow(position)
        }
    }

首先,将看到 expandParentRow 功能

科特林

private fun expandParentRow(position: Int){
       val currentBoardingRow = list[position]
       val services = currentBoardingRow.subList
       currentBoardingRow.isExpanded = true
       var nextPosition = position
       if(currentBoardingRow.type==Constants.PARENT){
 
           services.forEach { service ->
               val parentModel =  ParentData()
               parentModel.type = Constants.CHILD
               val subList : ArrayList = ArrayList()
               subList.add(service)
               parentModel.subList=subList
               list.add(++nextPosition,parentModel)
           }
           notifyDataSetChanged()
       }
   }

让我们看看,当使用位置调用 expandParentRow函数时,扩展逻辑是如何工作的。我们从获得子列表的位置获取数据。我们再次使用 for each 添加子列表项,并使用 +1 增加当前位置,如下所示

科特林

list.add(++nextPosition,parentModel)

在这里,我们将 parentModel 添加到列表中,并将类型添加为 Constants.CHILD,在添加了我们调用 notifyDataSetChanged() 的项目之后,因此回收器视图将再次加载新数据。再次绑定数据时,它将检查类型和数据到其视图。相同的逻辑适用于折叠,我们将在那里进行相反的操作。最后,我们需要将此适配器添加到 RecyclerView,如下所示。

科特林

val exRecycleView = findViewById(R.id.exRecycle)
        exRecycleView.layoutManager = LinearLayoutManager(this)
        exRecycleView.adapter = RecycleAdapter(this@MainActivity,listData)

和我们添加回收器视图的 main_layout.xml 文件。

XML



  
  
    
  

输出:

输出