RecyclerView(2) - 睡眠質(zhì)量 示例筆記

目的是 實(shí)現(xiàn) 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, 并調(diào)整布局

其中寬高都設(shè)置為 0dp, 可以使得它占滿所在的位置.

2. 在XML 中, 為 RecylcerView 添加一個(gè) 布局管理器 , 例如 LinearLayoutManager

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

3. 創(chuàng)建列表項(xiàng)布局 和 文本 ViewHolder

RecyclerView 只是一個(gè)容器,
需要
(1)創(chuàng)建 RecyclerView 中顯示的項(xiàng)布局和基礎(chǔ)架構(gòu)。
(2)ViewHolder - 緩存項(xiàng)視圖.

簡(jiǎn)單實(shí)現(xiàn):
(1) text_item_view.xml 僅包含一個(gè)TextView
(2) 創(chuàng)建一個(gè)簡(jiǎn)單的 TextItemViewHolder 繼承自 RecyclerView.ViewHolder

4. 創(chuàng)建適配器 SleepNightAdapter

實(shí)現(xiàn) RecyclerView 時(shí),核心任務(wù)就是創(chuàng)建適配器。
項(xiàng)視圖(ItemView)有一個(gè)簡(jiǎn)單的 ViewHolder,并且每個(gè)項(xiàng)都有一個(gè)布局(item_view_layout.xml)。
適配器會(huì)創(chuàng)建一個(gè)ViewHolder,并在其中填充數(shù)據(jù)以供 RecyclerView 顯示.

4.1 定義數(shù)據(jù)**

例如

var data =  listOf<SleepNight>()

延伸, 需要在data 發(fā)生變更時(shí) 通知 RecyclerView, 因?yàn)?RecyclerView 對(duì)數(shù)據(jù)一無(wú)所知, 它只了解提供的ViewHolder.
因此,可以對(duì) data 添加 setter 中(該代碼塊在 SleepNightAdapter 頂部)

即:

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

注意:調(diào)用 notifyDataSetChanged() 后,RecyclerView 會(huì)重新繪制整個(gè)列表,而不只是已更改的項(xiàng)。

4.2 實(shí)現(xiàn)三個(gè)函數(shù)**

(1)getItemCount 獲取顯示的數(shù)據(jù)項(xiàng)數(shù)目

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

(2)onCreateViewHolder 創(chuàng)建 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 參數(shù)(即用于容納 ViewHolder 的視圖組);
這里需要使用 parent 獲取 布局加載器, 通過(guò)它加載 項(xiàng)布局

(3)onBindViewHolder 為ViewHolder綁定數(shù)據(jù)

    override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
        val item = data[position]
        holder.textView.text = item.sleepQuality.toString()// 僅顯示 睡眠質(zhì)量數(shù)值
    }

5. 為 RecyclerView 設(shè)置 適配器.

先創(chuàng)建適配器, 然后將 其設(shè)置給 RecyclerView.

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

6. 將數(shù)據(jù)獲取到適配器中

監(jiān)聽ViewModel 中的數(shù)據(jù)變化(LiveData) 并 設(shè)置到 適配器中.

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

至此,可以簡(jiǎn)單的顯示 睡眠質(zhì)量 的 評(píng)分?jǐn)?shù)據(jù), 未包含所有數(shù)據(jù)

6. 顯示所有睡眠數(shù)據(jù)

包括 睡眠質(zhì)量圖片、睡眠事件、睡眠質(zhì)量感受等,
這里需要修改 每一項(xiàng)的布局文件 、重新定義ViewHolder 、對(duì)應(yīng)修改創(chuàng)建 和綁定ViewHolder.

7. 優(yōu)化代碼

這部分是把 onBindViewHolder 和 onCreateViewHolder 中,和ViewHolder 相關(guān)的代碼,
都移到 ViewHolder 中
因?yàn)?ViewHolder 的實(shí)現(xiàn)可能是 經(jīng)常變化的(例如顯示 每一項(xiàng)的內(nèi)容變更了)

7.1 onBindViewHolder 簡(jiǎn)化邏輯(交給 ViewHolder)

把加載 每一項(xiàng)布局子View 的操作(作為成員變量XXX) 放在 ViewHolder 里,
并且 定義一個(gè) bind()函數(shù),它的參數(shù)就是數(shù)據(jù),用它來(lái)更新子View.
然后 onBindViewHolder 里用 holder.bind(xxx) 就行了.
例如:

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

這樣使得,顯示管理 ViewHolder 的代碼分離.

7.2 onCreateViewHolder 簡(jiǎn)化邏輯(交給ViewHolder)

同樣, 這里 加載 每一項(xiàng)布局 以及 創(chuàng)建 ViewHolder 的操作, 和 Adapter 關(guān)系不大,和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)
    }

所以,同樣可以 移動(dòng)到 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 的函數(shù)內(nèi)容,抽取為
from 函數(shù), 并 在 伴生對(duì)象 companion object 里.
(2) 構(gòu)造函數(shù)標(biāo)記 為 private constructor,
表示其它地方無(wú)法調(diào)用構(gòu)造函數(shù)創(chuàng)建.
(3) onCreateViewHolder 里的代碼就會(huì)非常簡(jiǎn)潔:

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

從這兩個(gè)優(yōu)化可以看出, 把屬于ViewHolder 的代碼集中在一起,使得 Adapter 代碼 更加簡(jiǎn)潔,
ViewHolder 的變化即不同顯示需求的實(shí)現(xiàn)變化,只需更改 ViewHolder 內(nèi)部代碼即可。
這是非常巧妙的設(shè)計(jì) (抽離變化, 高內(nèi)聚)

完整代碼參考:

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

--- End ---

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容