RecyclerView(2) - 睡眠质量 示例笔记

目的是 实现 RecyclerView 和适配器

参考指南 和 示例代码:
https://developer.android.com/codelabs/kotlin-android-training-recyclerview-fundamentals?index=..%2F..android-kotlin-fundamentals&hl=zh-cn#3
https://github.com/google-developer-training/android-kotlin-fundamentals-starter-apps/tree/master/RecyclerViewFundamentals-Starter

1. 布局中, 使用 RecylcerView 取代 ScrollView, 并调整布局

其中宽高都设置为 0dp, 可以使得它占满所在的位置.

2. 在XML 中, 为 RecylcerView 添加一个 布局管理器 , 例如 LinearLayoutManager

app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"

3. 创建列表项布局 和 文本 ViewHolder

RecyclerView 只是一个容器,
需要
(1)创建 RecyclerView 中显示的布局和基础架构。
(2)ViewHolder - 缓存项视图.

简单实现:
(1) text_item_view.xml 仅包含一个TextView
(2) 创建一个简单的 TextItemViewHolder 继承自 RecyclerView.ViewHolder

4. 创建适配器 SleepNightAdapter

实现 RecyclerView 时,核心任务就是创建适配器。
项视图(ItemView)有一个简单的 ViewHolder,并且每个项都有一个布局(item_view_layout.xml)。
适配器会创建一个ViewHolder,并在其中填充数据以供 RecyclerView 显示.

4.1 定义数据**

例如

var data =  listOf()

延伸, 需要在data 发生变更时 通知 RecyclerView, 因为 RecyclerView 对数据一无所知, 它只了解提供的ViewHolder.
因此,可以对 data 添加 setter 中(该代码块在 SleepNightAdapter 顶部)

即:

    var data = listOf()
        set(value) {
            field = value
            notifyDataSetChanged()
        }

注意:调用 notifyDataSetChanged() 后,RecyclerView 会重新绘制整个列表,而不只是已更改的项。

4.2 实现三个函数**

(1)getItemCount 获取显示的数据项数目

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

(2)onCreateViewHolder 创建 ViewHolder

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val view = layoutInflater.inflate(R.layout.text_item_view, parent,false) as TextView
        return TextItemViewHolder(view)
    }

parent 参数(即用于容纳 ViewHolder 的视图组);
这里需要使用 parent 获取 布局加载器, 通过它加载 项布局

(3)onBindViewHolder 为ViewHolder绑定数据

    override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
        val item = data[position]
        holder.textView.text = item.sleepQuality.toString()// 仅显示 睡眠质量数值
    }

5. 为 RecyclerView 设置 适配器.

先创建适配器, 然后将 其设置给 RecyclerView.

val adapter = SleepNightAdapter()
binding.sleepList.adapter = adapter

6. 将数据获取到适配器中

监听ViewModel 中的数据变化(LiveData) 并 设置到 适配器中.

        sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
            it?.let {
                adapter.data = it
            }
        })

至此,可以简单的显示 睡眠质量 的 评分数据, 未包含所有数据

6. 显示所有睡眠数据

包括 睡眠质量图片、睡眠事件、睡眠质量感受等,
这里需要修改 每一项的布局文件 、重新定义ViewHolder 、对应修改创建 和绑定ViewHolder.

7. 优化代码

这部分是把 onBindViewHolder 和 onCreateViewHolder 中,和ViewHolder 相关的代码,
都移到 ViewHolder 中
因为 ViewHolder 的实现可能是 经常变化的(例如显示 每一项的内容变更了)

7.1 onBindViewHolder 简化逻辑(交给 ViewHolder)

把加载 每一项布局子View 的操作(作为成员变量XXX) 放在 ViewHolder 里,
并且 定义一个 bind()函数,它的参数就是数据,用它来更新子View.
然后 onBindViewHolder 里用 holder.bind(xxx) 就行了.
例如:

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = data[position]
        holder.bind(item)
    }

这样使得,显示管理 ViewHolder 的代码分离.

7.2 onCreateViewHolder 简化逻辑(交给ViewHolder)

同样, 这里 加载 每一项布局 以及 创建 ViewHolder 的操作, 和 Adapter 关系不大,和ViewHolder 紧密相连。

    override fun onCreateViewHolder(
            parent: ViewGroup, viewType: Int): ViewHolder {
        val layoutInflater =
            LayoutInflater.from(parent.context)
        val view = layoutInflater
                .inflate(R.layout.list_item_sleep_night,
                         parent, false)
        return ViewHolder(view)
    }

所以,同样可以 移动到 ViewHolder里.

    class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
        ....

        companion object{
            fun from(parent: ViewGroup): ViewHolder {
                val layoutInflater = LayoutInflater.from(parent.context)
                val view = layoutInflater.inflate(R.layout.list_item_sleep_night, parent, false)
                return ViewHolder(view)
            }
        }

分析:
(1) 将 onCreateViewHolder 的函数内容,抽取为
from 函数, 并 在 伴生对象 companion object 里.
(2) 构造函数标记 为 private constructor,
表示其它地方无法调用构造函数创建.
(3) onCreateViewHolder 里的代码就会非常简洁:

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder.from(parent)
    }

从这两个优化可以看出, 把属于ViewHolder 的代码集中在一起,使得 Adapter 代码 更加简洁,
ViewHolder 的变化即不同显示需求的实现变化,只需更改 ViewHolder 内部代码即可。
这是非常巧妙的设计 (抽离变化高内聚)

完整代码参考:

https://github.com/google-developer-training/android-kotlin-fundamentals-apps/tree/master/RecyclerViewFundamentals

--- End ---

你可能感兴趣的:(RecyclerView(2) - 睡眠质量 示例笔记)