使用 Kotlin 在 Android 中扩展 RecyclerView
在本文中,我们将了解如何实现可扩展的 RecycleView。一般来说,我们会在列表视图中显示项目列表,但在某些情况下,如果您想显示子列表项目,那么我们必须使用可扩展列表。请参阅下图以获得更好的理解。
分步实施
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
输出: