前言
因為APP設(shè)計的原因,Recyclerview是我在Android中最常用的組件灾馒,我們公司的APP幾乎每一個頁面都會包含至少一個Recyclerview,本篇文章主要介紹一些我個人在工作中總結(jié)、收集的recyclerview優(yōu)化經(jīng)驗蕴掏。
正文
1.不要在onBindViewHolder中設(shè)置點擊事件和耗時操作
Recyclerview的onBindViewHoler主要負責(zé)將數(shù)據(jù)與holder綁定蹬挤,它在列表滑動時會不停的被調(diào)用缚窿。如果在onBindViewHolder中設(shè)定監(jiān)聽操作,會導(dǎo)致已經(jīng)的綁定點擊事件的view焰扳,被重復(fù)綁定監(jiān)聽操作倦零。
點擊事件的監(jiān)聽可以在onCreateViewHolder中設(shè)定。一些會創(chuàng)建新對象的操作吨悍,也需要根據(jù)實際情況考慮從onBindViewHolder中遷移到onCreateViewHolder扫茅。
注意: onBindViewHolder運行在UI線程中,如果進行了耗時操作育瓜,會導(dǎo)致頁面卡頓葫隙。并且onBindViewHolder中只應(yīng)該進行數(shù)據(jù)的綁定,而不應(yīng)該進行數(shù)據(jù)的處理和計算等操作躏仇。
2.Recyclerview嵌套Recyclerview的優(yōu)化
Recyclerview嵌套Recyclerview最經(jīng)典的運用就是恋脚,一個縱向滑動的列表內(nèi)部的每個item是一個可以橫向滑動的Recyclerview腺办,比如說GooglePlay。
這種情況可以使用LinearLayoutManager.setInitialPrefetchItemCount()設(shè)定
橫向列表初次顯示時可見item的個數(shù)糟描。如果橫向滑動的View中數(shù)據(jù)量很少怀喉,并且不需要橫向刷新時,也可以考慮使用HorizontalScrollView實現(xiàn)船响。
關(guān)于這個API的官方文檔翻譯如下:
設(shè)置collectInitialPrefetchPositions(int躬拢,LayoutPrefetchRegistry)中要預(yù)取的項目數(shù),它定義當此LayoutManager的RecyclerView嵌套在另一個RecyclerView中時應(yīng)預(yù)取多少內(nèi)部項目灿意。
將此值設(shè)置為此內(nèi)部LayoutManager首次滾動到視口時將顯示的項目數(shù)估灿。 RecyclerView將嘗試預(yù)取該數(shù)量的項目,并提前將這些Item初始化缤剧,避免因內(nèi)部RecyclerView滾動到視圖(viewport)中變得混亂馅袁。
舉個例子,水平滾動的RecyclerViews內(nèi)部嵌套垂直滾動的RecyclerView荒辕。行中始終有4個項目可見(如果沒有對齊汗销,則是5個)。為每個內(nèi)部的RecyclerView的LinearLayoutManager設(shè)定setInitialPrefetchItemCount=4抵窒,這將使RecyclerView的預(yù)取功能能夠在屏幕上滾動之前弛针,提前為一行中的4個視圖完成創(chuàng)建/綁定工作。
只有在一個Recyclerview嵌套在另一個Recyclerview中李皇,設(shè)定collectInitialPrefetchPositions才會生效削茁。
如果將此值設(shè)置為大于此視圖中可見的視圖數(shù)可能會導(dǎo)致不必要的綁定工作,并且會增加創(chuàng)建和活動使用的視圖數(shù)掉房。
3.多個RecycerView共用RecycledViewPool
在大多數(shù)APP中都有這樣一種場景茧跋,一個ViewPager中包含多個Fragment,而Fragment中主體是Recyclerview卓囚,并且Recyclerview中item view的布局是相同的瘾杭。例如 微博等
這種情況下,Recyclerview可以設(shè)定統(tǒng)一的緩存池用來提高性能哪亿。
新建緩存池:
RecyclerView.RecycledViewPool viewPool=new RecyclerView.RecycledViewPool();
設(shè)定緩存池:
recyclerView.setRecycledViewPool(viewPool);
4.精確的更新數(shù)據(jù)使用DiffUtil
在Recyclerview中提供了多種數(shù)據(jù)數(shù)據(jù)刷新方式
- notifyDataChanged()
用于刷新全部數(shù)據(jù)粥烁,會導(dǎo)致整個布局重繪,所有的ViewHolder也會重新綁定蝇棉。 - notifyItemXXX();
用于刷新指定位置的Item數(shù)據(jù)讨阻。
雖然有了這些刷新方式,但是實際開發(fā)中篡殷,存在這樣一種情況变勇,新數(shù)據(jù)集與舊數(shù)據(jù)集僅有一部分數(shù)據(jù)存在差異。
例如:刷新一個聯(lián)系人列表,聯(lián)系人列表中部分聯(lián)系人的頭像有變化搀绣,但是姓名和手機號碼等信息未發(fā)生變化。這種情況以往都是使用notifyDataChanged方法刷新全部數(shù)據(jù)戳气,但是刷新全部數(shù)據(jù)的會導(dǎo)致整個布局重繪链患,Recyclerview中針對這種情況還提供了另一種粒度更小的刷新方式DiffUti
這里不打算去講DiffUtil的具體用法,只需要記住DiffUtil的使用場景即可:列表中存在多個Item的數(shù)據(jù)需要刷新瓶您,但是新數(shù)據(jù)集與舊數(shù)據(jù)集存在重復(fù)的情況
5.靈活設(shè)定setHasFixedSize
在Recyclerview中使用以下方法時麻捻,會觸發(fā)requestLayout()
- onItemRangeChanged()
- onItemRangeInserted()
- onItemRangeRemoved()
- onItemRangeMoved()
requestLayout()會重新計算item的大小,如果item的布局文件已經(jīng)將寬高設(shè)為固定大小呀袱,可以設(shè)定setHasFixedSize(true)贸毕,來避免Recyclerview重新計算item的大小。
6.優(yōu)化Item的布局
布局優(yōu)化是個老生長談的問題夜赵,本質(zhì)上就是減少嵌套明棍,ConstraintLayout是google推出的一個用于減少布局嵌套的新layout,但是在Recyclerview中使用ConstraintLayout會導(dǎo)致cpu使用率上升寇僧,暫時不推薦使用摊腋,不過ConstraintLayout 2.0版本已經(jīng)進入beta測試,期待后續(xù)會有優(yōu)化嘁傀。
7.數(shù)據(jù)非常少時兴蒸,使用ListView
不知道你有沒有考慮過這樣的問題,RecyclerView用已經(jīng)如此強大细办,用得人也越來越多橙凳,為什么最新的Android系統(tǒng)中ListView依然沒有被標注為"過時"。
RecyclerView重新設(shè)計了緩存機制笑撞,新的緩存機制更加強大岛啸,更適合處理大量數(shù)據(jù)的顯示,但是這種緩存機制會占據(jù)更多的內(nèi)存娃殖,當一個列表頁面實際數(shù)據(jù)項非常少的時候值戳,ListView往往比RecyclerView更合適。