后端 Day12 Nested RecyclerView(番外篇)

bunnypek · August 12, 2021 · 0 hits

关于这个番外篇呢
其实跟这次铁人赛的主题关联性较低
没兴趣的可以先行跳过

写这篇的原因是快富坚了 只好先塞其他内容垫档一下

今天要举的例子是这个
https://d1dwq032kyr03c.cloudfront.net/upload/images/20190927/201202795BkOdNwOrt.jpg
(图取自 google play)

现在很多 app 的首页都有类似这种嵌套式 UI 你分明就是想拿上班的工作项目来混水摸鱼
那这个需求其实可以用内置的 RecyclerView 来实现

今天会创建新的项目来实做类似的页面

解决方案放在这
https://github.com/mars1120/NestedRecyclerViewDemo

先设置 Dependencies
build.gradle

Dependencies{
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0'
}

首先先创建垂直的 recyclerview

先从首页的 xml 开始
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:id="@+id/rv_base"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

</androidx.constraintlayout.widget.ConstraintLayout>

接着创建可重复添加的 recyclerview
水平的迁套 recyclerview 之后也会添加在这 xml 之内

view_item_parent_recycler.xml

<androidx.cardview.widget.CardView
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/card_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_margin="2dp"
    card_view:cardBackgroundColor="#fff"
    card_view:cardCornerRadius="5dp"

    card_view:cardElevation="4dp"
    card_view:cardUseCompatPadding="true">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_title"
            style="@style/Base.TextAppearance.AppCompat.Subhead"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignStart="@+id/rv_child"
            android:layout_alignParentTop="true"
            android:padding="20dp"
            android:text="I'm title"
            android:textColor="@color/colorAccent"
            android:textStyle="bold" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_child"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="30dp"
            android:orientation="horizontal"
            android:padding="20dp"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            tools:layout_editor_absoluteX="74dp" />

    </RelativeLayout>

</androidx.cardview.widget.CardView>

另一个类型的 item
view_banner.xml

<androidx.cardview.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/card_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_margin="2dp"
    card_view:cardBackgroundColor="#fff"
    card_view:cardCornerRadius="5dp"

    card_view:cardElevation="4dp"
    card_view:cardUseCompatPadding="true">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:background="@color/cardview_dark_background"
            android:id="@+id/tv_banner"
            style="@style/Base.TextAppearance.AppCompat.Subhead"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignStart="@+id/rv_child"
            android:layout_alignParentTop="true"
            android:layout_alignParentEnd="true"
            android:gravity="center"
            android:padding="40dp"
            android:text="I'm Banner"
            android:textColor="@color/colorPrimary"
            android:textStyle="bold" />


    </RelativeLayout>

</androidx.cardview.widget.CardView>

ItemParentModel
用来存数据的数据类

ItemParentModel.kt

data class ItemParentModel (
    val title : String = ""
)

ItemParentAdapter
用来绑定与设置 recyclerView 用的

ItemParentAdapter.kt

class ItemParentAdapter(private val parents: List<ItemParentModel>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup,
                                    viewType: Int): RecyclerView.ViewHolder {

        when (viewType) {
            0 -> return BannerViewHolder(
                LayoutInflater.from(parent.context)
                    .inflate(R.layout.view_banner, parent, false))
            else -> return ItemViewHolder(
                LayoutInflater.from(parent.context)
                    .inflate(R.layout.view_item_parent_recycler, parent, false))
        }

    }

    override fun getItemCount(): Int {
        return parents.size+1
    }

    override fun getItemViewType(position: Int): Int {
        return if (0 == position) {
            TYPE_BANNER
        } else {
            TYPE_ITEM
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder,
                                  position: Int) {

        when(holder)
        {
            is BannerViewHolder -> {
                holder.textView.text = "hello"
            }
            is ItemViewHolder -> {
                holder.textView.text =  parents[position-1].title
            }
        }
    }

    private inner class BannerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val textView: TextView = itemView.tv_banner
    }

    private inner class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val textView: TextView = itemView.tv_title
    }

    companion object {
        private val TYPE_BANNER = 0
        private val TYPE_ITEM = 1
    }
}

最后是首页
Mainactivity.kt

class MainActivity : AppCompatActivity() {

    lateinit var recyclerView: RecyclerView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        recyclerView = rv_base
        recyclerView.apply {
            adapter = ItemParentAdapter(getParents())
        }
    }
    private fun getParents(): List<ItemParentModel> {
        val listOfFacility = mutableListOf<ItemParentModel>()
        var facilityModel = ItemParentModel("Sample 0")
        listOfFacility.add(facilityModel)
        facilityModel = ItemParentModel("Sample 1")
        listOfFacility.add(facilityModel)
        facilityModel = ItemParentModel("Sample 2")
        listOfFacility.add(facilityModel)
        facilityModel = ItemParentModel("Sample 3")
        listOfFacility.add(facilityModel)
        facilityModel = ItemParentModel("Sample 4")
        listOfFacility.add(facilityModel)
        facilityModel = ItemParentModel("Sample 5")
        listOfFacility.add(facilityModel)
        facilityModel = ItemParentModel("Sample 6")
        listOfFacility.add(facilityModel)
        return listOfFacility
    }
}

目前阶段的画面

https://d1dwq032kyr03c.cloudfront.net/upload/images/20190927/20120279YgQ1rZaKF3.png

接下来会调整 ItemParentAdapter 让他再嵌入一个 recyclerview

首先先来添加 child xml
view_item_child_recycler.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:orientation="horizontal"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/child_textView"
        android:layout_width="128dp"
        android:layout_height="37dp"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="1dp"
        android:background="@android:color/darker_gray"
        android:padding="10dp"
        android:text="TextView"
        android:textColor="@android:color/white"
        android:textStyle="bold"
        app:layout_constraintBottom_toTopOf="@id/child_imageView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0" />

    <ImageView
        android:id="@+id/child_imageView"
        android:layout_width="126dp"
        android:layout_height="189dp"
        android:layout_marginBottom="38dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="42dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1.0"
        app:srcCompat="@drawable/ic_attach_money_128dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

其中@drawable/ic_attach_money_128dp 可添加矢量图或用其他图源代替

添加 child 数据源
ChildModel.kt

data class ChildModel(
    val image: Int = -1,
    val title: String = ""
)

ChildAdapter.kt

class ChildAdapter(private val children: List<ChildModel>) :
    RecyclerView.Adapter<ChildAdapter.ViewHolder>() {

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): ViewHolder {

        val v = LayoutInflater.from(parent.context)
            .inflate(R.layout.view_item_child_recycler, parent, false)
        return ViewHolder(v)
    }

    override fun getItemCount(): Int {
        return children.size
    }

    override fun onBindViewHolder(
        holder: ViewHolder,
        position: Int
    ) {
        val child = children[position]
        holder.imageView.setImageResource(child.image)
        holder.textView.text = child.title
    }


    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        val textView: TextView = itemView.child_textView
        val imageView: ImageView = itemView.child_imageView

    }
}

接着要回头修改 ItemParentAdapter 串接 child RecyclerView

ItemParentAdapter.kt

class ItemParentAdapter( ...
...
 private val viewPool = RecyclerView.RecycledViewPool()
   override fun onBindViewHolder() {
        when(holder)
        {
            is ItemViewHolder -> {
                holder.textView.text =  parents[position-1].title

                holder.recyclerView.apply {
                    adapter = ChildAdapter(parents[position-1].children)
                    setRecycledViewPool(viewPool)
                }
            }
        }
    }
    
 private inner class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val textView: TextView = itemView.tv_title
        val recyclerView : RecyclerView = itemView.rv_child
    }

父对象的数据源也要修改
data class ItemParentModel (
val title : String = "",
val children : List
)

最后是调整后的首页
MainActivity.kt

class MainActivity : AppCompatActivity() {

    lateinit var recyclerView: RecyclerView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        recyclerView = rv_base
        recyclerView.apply {
            adapter = HomeItemParentAdapter(getParents())
        }
    }

    private fun getParents(): List<ItemParentModel> {
        val listOfFacility = mutableListOf<ItemParentModel>()
        var facilityModel = ItemParentModel("Sample 0", ChildDataFactory.getChildren(5))
        listOfFacility.add(facilityModel)

        facilityModel = ItemParentModel("Sample 1", ChildDataFactory.getChildren(5))
        listOfFacility.add(facilityModel)
        facilityModel = ItemParentModel("Sample 2", ChildDataFactory.getChildren(5))
        listOfFacility.add(facilityModel)
        facilityModel = ItemParentModel("Sample 3", ChildDataFactory.getChildren(5))
        listOfFacility.add(facilityModel)
        facilityModel = ItemParentModel("Sample 4", ChildDataFactory.getChildren(5))
        listOfFacility.add(facilityModel)
        facilityModel = ItemParentModel("Sample 5", ChildDataFactory.getChildren(5))
        listOfFacility.add(facilityModel)
        facilityModel = ItemParentModel("Sample 6", ChildDataFactory.getChildren(5))
        listOfFacility.add(facilityModel)

        return listOfFacility
    }

    object ChildDataFactory {
        private val random = Random()
        private val titles = arrayListOf("title0", "title1", "title2", "title3", "title4")
        private fun randomTitle(): String {
            val index = random.nextInt(titles.size)
            return titles[index]
        }
        private fun randomImage(): Int {
            return R.drawable.ic_attach_money_128dp
        }
        fun getChildren(count: Int): List<ChildModel> {
            val children = mutableListOf<ChildModel>()
            repeat(count) {
                val child = ChildModel(randomImage(), randomTitle())
                children.add(child)
            }
            return children
        }
    }
}

搞定

成品画面
https://d1dwq032kyr03c.cloudfront.net/upload/images/20190927/20120279QWnrVoM2qy.png

最后再贴一次 solution 链接

https://github.com/mars1120/NestedRecyclerViewDemo

之后可能会拿本项目当基底做一些其他 demo


No Reply at the moment.
You need to Sign in before reply, if you don't have an account, please Sign up first.