【UI篇3】RecyclerView創(chuàng)建動(dòng)態(tài)列表

前言
RecyclerView是一款功能十分強(qiáng)大的widget顷牌,涉及到列表呈現(xiàn)時(shí)帜平,通常都會(huì)使用到RecyclerView瀑焦,功能如此強(qiáng)大的widget不做一下記錄實(shí)在可惜蔫浆。

  • Android官方指導(dǎo)文檔有詳細(xì)的介紹如何使用RecyclerView創(chuàng)建動(dòng)態(tài)列表碑宴,具體參考如下地址
    https://developer.android.com/guide/topics/ui/layout/recyclerview?hl=zh-cn

  • 思考任何問(wèn)題時(shí)软啼,我們都需要回到問(wèn)題的本質(zhì)。通過(guò)RecyclerView 的源碼我們可以知道RecyclerView是一個(gè)容器延柠,用于顯示列表或網(wǎng)格形式的數(shù)據(jù)祸挪,比如文本或照片。

1. 實(shí)現(xiàn)RecyclerView

1.1 RecyclerView布局
  • 布局
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:contentDescription="@string/fab_content_description"
        android:src="@drawable/ic_add_black_24dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

Activity中的設(shè)置

  • Activity
//初始使用
mRecyclerView = findViewById(R.id.recycler_view)
mRecyclerView.layoutManager = LinearLayoutManager(this)
mRecyclerView.adapter = mAdapter
1.2 規(guī)劃布局

組成RecyclerView的表項(xiàng)可能有多種類型贞间,不同的類型會(huì)有不同的布局贿条。根據(jù)列表或網(wǎng)格的外觀,確定布局管理器增热,RecyclerView中的列表項(xiàng)由LayoutManager類負(fù)責(zé)排列整以。

  • 可以擴(kuò)展RecyclerView.LayoutManager抽象類來(lái)創(chuàng)建自己的布局管理器
  • 可以擴(kuò)展RecyclerView.ItemAnimator來(lái)定義自己的animator對(duì)象
  • 可以擴(kuò)展RecyclerView.ItemDecoration來(lái)定義自己的分割線

同一個(gè)RecyclerView可以綁定不同ViewType類型的Item,可以定義接口如下峻仇,其他Item實(shí)現(xiàn)該接口

interface IWallpaperListItem {
    val type: Int
}

class SourceItem : IWallpaperListItem {
    override val type: Int
        get() = ViewType.SOURCE
}

class RecommendItem(
    val imageList: List<Image>? = null
) : IWallpaperListItem {
    override val type: Int
        get() =ViewType.WALLPAPER
}
1.3 實(shí)現(xiàn)Adapter和ViewHolder
  • a. 創(chuàng)建Adapter
    只定義名字
  • b. 創(chuàng)建ViewHolder
    創(chuàng)建ViewHolder的內(nèi)部類公黑,并且它可以接收一個(gè)itemView作為參數(shù)。然后創(chuàng)建bind函數(shù)础浮,將數(shù)據(jù)和攜帶數(shù)據(jù)的UI關(guān)聯(lián)起來(lái)帆调。
abstract class AbsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    abstract fun bind(wallpaperListItem: IWallpaperListItem)
}

class ***TitleViewHolder(itemView: View) : AbsViewHolder(itemView) {
    override fun bind(wallpaperListItem: IWallpaperListItem) {
        //綁定內(nèi)容
    }
}

class ***SourceViewHolder(itemView: View) : AbsViewHolder(itemView) {
    private val ***CardView = itemView.findViewById<CardView>(R.id.***_card_view)
    private val ***CardView = itemView.findViewById<CardView>(R.id.***_card_view)
    init {
                //設(shè)置背景
        //CardView.background = 
        ***CardView.setOnClickListener {
            Log.d(TAG, "onClick")
            //點(diǎn)擊
        }

        //***View.background = 
        ***CardView.setOnClickListener {
            Log.d(TAG, "onClick")
            //點(diǎn)擊
        }
    }

    override fun bind(wallpaperListItem: IWallpaperListItem) {
        //綁定
    }

}
  • c. 繼承RecyclerView.Adapter
    更新Adapter類的定義,使其繼承RecyclerView.Adapter類豆同,并且將ViewHolder作為參數(shù)傳入
    這里也可以改為繼承ListAdapter
class ***Adapter(
    private val retryLoad: () -> Unit
) : ListAdapter<IWallpaperListItem, ***.AbsViewHolder>(***DiffCallback) {
  • d. 重寫(xiě)onCreateViewHolder()
    當(dāng)ViewHolder創(chuàng)建的時(shí)候會(huì)調(diào)用該方法番刊。在該方法里進(jìn)行初始化和填充RecyclerView中的表項(xiàng)視圖。
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AbsViewHolder {
        Log.d(TAG, "onBindViewHolder viewType=$viewType")
        return when (viewType) {
            ViewType.SOURCE -> {
                val view = LayoutInflater.from(parent.context).inflate(R.layout.1_item, parent, false)
                ***SourceViewHolder(view)
            }

            ViewType.TITLE -> {
                val view = LayoutInflater.from(parent.context).inflate(R.layout.2_title_item, parent, false)
                ***TitleViewHolder(view)
            }

            ViewType.WALLPAPER -> {
                val view = LayoutInflater.from(parent.context).inflate(R.layout.3_recommend_item, parent, false)
                ***WallpaperViewHolder(view) { retryLoad() }
            }

            else -> {
                throw IllegalStateException("view type is error")
            }
        }
    }
  • e. 重寫(xiě)onBindViewHolder()
    onBindViewHolder()被調(diào)用的時(shí)候影锈,會(huì)傳入?yún)?shù)ViewHolder和位置position芹务,該位置可以用于提取表項(xiàng)所需的數(shù)據(jù),并且將數(shù)據(jù)傳遞給ViewHolder來(lái)使數(shù)據(jù)綁定到對(duì)應(yīng)的UI鸭廷。
override fun onBindViewHolder(holder: AbsViewHolder, position: Int) {
        Log.d(TAG, "onBindViewHolder position=$position")
        val ***ListItem = getItem(position)
        holder.bind(***ListItem)
    }
  • f. 重寫(xiě)getItemCount()
    RecyclerView顯示一個(gè)列表枣抱,所以它需要知道列表里共有多少項(xiàng)。

ListAdapter可以處理元素的添加和刪除而無(wú)需重繪視圖辆床,甚至可以為變化添加動(dòng)畫(huà)效果佳晶。
DiffUtil 是 ListAdapter 能夠高效改變?cè)氐膴W秘所在。DiffUtil 會(huì)比較新舊列表中增加讼载、移動(dòng)轿秧、刪除了哪些元素中跌,然后輸出更新操作的列表將原列表中的元素高效地轉(zhuǎn)換為新的元素。

為了能夠識(shí)別新的數(shù)據(jù)菇篡,DiffUtil 需要您重寫(xiě) areItemsTheSame() 和 areContentsTheSame()漩符。areItemsTheSame() 檢查兩個(gè)元素是否為同一元素。areContentsTheSame() 檢查兩個(gè)元素是否包含相同的數(shù)據(jù)驱还。

在Adapter類中添加DiffUtil對(duì)象嗜暴,并且復(fù)寫(xiě) areItemsTheSame()和areContentsTheSame()

object FlowerDiffCallback : DiffUtil.ItemCallback<Flower>() {
    override fun areItemsTheSame(oldItem: Flower, newItem: Flower): Boolean {
        return oldItem == newItem
    }

    override fun areContentsTheSame(oldItem: Flower, newItem: Flower): Boolean {
        return oldItem.id == newItem.id
    }
}

將Adapter的父類由RecyclerView.Adapter改為L(zhǎng)istAdapter,并傳入DiffCallback

class FlowersAdapter(private val onClick: (Flower) -> Unit) :
    ListAdapter<Flower, FlowersAdapter.FlowerViewHolder>(FlowerDiffCallback) {

更新列表
ListAdapter 通過(guò) submitList() 方法獲取數(shù)據(jù)议蟆,該方法提交了一個(gè)列表來(lái)與當(dāng)前列表進(jìn)行對(duì)比并顯示闷沥。也就是說(shuō)您無(wú)需再重寫(xiě) getItemCount(),因?yàn)?ListAdapter 會(huì)負(fù)責(zé)管理列表咪鲜。

在 Activity 類中狐赡,調(diào)用 Adapter 的 submitList() 方法并傳入數(shù)據(jù)列表撞鹉。
此處需要通過(guò)toMutableList轉(zhuǎn)換生成一個(gè)新的list疟丙,否則不會(huì)更新,不同的item鸟雏,viewType不一樣享郊。

private fun updateListData(imageList: List<ImageData>?) {
        Log.d(TAG, "updateListData imageList.size=${imageList?.size}")
        mItemList.clear()
        mItemList.add(SourceItem())
        mItemList.add(TitleItem())
        mItemList.add(RecommendItem(imageList))
        mAddAdapter.submitList(mItemList.toMutableList())
    }
1.4 連接

我們已經(jīng)創(chuàng)建了布局、數(shù)據(jù)列表和adapter孝鹊,可以直接將adapter賦給RecyclerView

2 響應(yīng)點(diǎn)擊事件

2.1 定義點(diǎn)擊動(dòng)作

在創(chuàng)建監(jiān)聽(tīng)器之前炊琉,在 Activity 類中添加一個(gè)函數(shù)用于處理點(diǎn)擊之后的響應(yīng)操作。

private fun adapterOnClick() {
   val intent = Intent(this,DetailActivity()::class.java)
   this.startActivity(intent)
}

接下來(lái)又活,修改 Adapter 的構(gòu)造函數(shù)來(lái)傳入 onClick() 函數(shù)苔咪。

class **Adapter(private val onClick: (Flower) -> Unit) :
  ListAdapter<T, RecyclerView.ViewHolder>(DiffCallback())

在 Activity 類中,在初始化 Adapter 的時(shí)候傳入剛剛創(chuàng)建的點(diǎn)擊事件函數(shù)柳骄。

2.2 添加onClickHandler()

現(xiàn)在響應(yīng)處理已經(jīng)定義好了团赏,可以將它關(guān)聯(lián)到 Adapter 的 ViewHolder 了。

修改 ViewHolder耐薯,將 onClick() 作為參數(shù)傳入舔清。

class ViewHolder(itemView: View, val onClick: (Source) -> Unit) :
  RecyclerView.ViewHolder(itemView)

在初始化的代碼中,調(diào)用 itemView 的 setOnClickListener{}

init {
    itemView.setOnClickListener {
       currentItem?.let {
          onClick(it)
       }
    }
}

現(xiàn)在就可以響應(yīng)點(diǎn)擊事件了

3 ConcatAdapter

使用ConcatAdapter可以將多個(gè)adapter添加到RecyclerView曲初,ConcatAdapter會(huì)依次顯示多個(gè)Adapter的內(nèi)容体谒,有興趣可以了解

4 參考資料

https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzk0NDIwMTExNw%3D%3D&action=getalbum&album_id=1866978093609893891#wechat_redirect

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市臼婆,隨后出現(xiàn)的幾起案子抒痒,更是在濱河造成了極大的恐慌,老刑警劉巖颁褂,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件故响,死亡現(xiàn)場(chǎng)離奇詭異纷捞,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)被去,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)主儡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人惨缆,你說(shuō)我怎么就攤上這事糜值。” “怎么了坯墨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵寂汇,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我捣染,道長(zhǎng)骄瓣,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任耍攘,我火速辦了婚禮榕栏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蕾各。我一直安慰自己扒磁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布式曲。 她就那樣靜靜地躺著妨托,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吝羞。 梳的紋絲不亂的頭發(fā)上兰伤,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音钧排,去河邊找鬼敦腔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛卖氨,可吹牛的內(nèi)容都是我干的会烙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼筒捺,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼柏腻!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起系吭,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤五嫂,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體沃缘,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躯枢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了槐臀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锄蹂。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖水慨,靈堂內(nèi)的尸體忽然破棺而出得糜,到底是詐尸還是另有隱情,我是刑警寧澤晰洒,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布朝抖,位于F島的核電站,受9級(jí)特大地震影響谍珊,放射性物質(zhì)發(fā)生泄漏治宣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一砌滞、第九天 我趴在偏房一處隱蔽的房頂上張望侮邀。 院中可真熱鬧,春花似錦布持、人聲如沸豌拙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至捉超,卻和暖如春胧卤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拼岳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工枝誊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人惜纸。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓叶撒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親耐版。 傳聞我的和親對(duì)象是個(gè)殘疾皇子祠够,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容