RecyclerView 體驗優(yōu)化及入坑總結(jié)
來源:原創(chuàng) 時間:2018-01-29 瀏覽:0 次前語
本文所講RecyclerView 是來自support 庫 26 版別,本文首要來源于本身開發(fā)及組內(nèi)搭檔遇到問題的經(jīng)驗總結(jié),作為常識沉積記載一下,以備日后檢查。
本文首要解說以下幾部分:
1.RecyclerView 滑動體會篇
橫向ViewPager與內(nèi)嵌橫向RecyclerView之間的滑動抵觸;
縱向RecycleView/ListView與橫向RecycleView之間的滑動抵觸;
橫向RecyclerView ItemView滑動不斷留在中心態(tài);
記載、康復(fù)RecyclerView翻滾偏移方位;
2.RecyclerView 入坑篇
RecyclerView導(dǎo)致的內(nèi)存走漏(support 26 + 7.0以下機(jī)型);
RecyclerView調(diào)用notifyDataSetChanged 會閃耀;
RecycleView/ListView設(shè)置itemView 為View.GONE 作用等同于View.Invisible;
RecycleView滑動體會
1.ViewPager與橫向RecyclerView之間的滑動抵觸
現(xiàn)在,企鵝FM項目中,許多頁面運(yùn)用ViewPager+ TabLayout (如主頁、詳情頁、查找成果頁等),而對應(yīng)頁面許多時分會嵌套一個橫向RecycleView,用來展示更多的信息,如下,在RecycleView中滑動到最終一個元素時,會一起帶動ViewPager滑動,這種體會極差。
原因剖析:
作為子View 的RecyclerView在滑到最終一個或第一個ItemView到導(dǎo)致ViewPager滑動,這必定是ViewPager在此刻對滑動事情進(jìn)行了阻攔,處理的最簡略辦法就是不讓ViewPager阻攔橫向RecyclerView的滑動事件(即ViewPager::onInterceptTouchEvent辦法回來false),ViewPager::onInterceptTouchEvent中的Move 事件如下:
現(xiàn)在,有以下兩種辦法使ViewPager 不去阻攔橫向RecyclerView 滑動事情:
1).在RecyclerView 對應(yīng)滑動事情分發(fā)中調(diào)用
getParent().requestDisallowInterceptTouchEvent(true); 阻撓ViewPager對其MOVE或許UP事情進(jìn)行阻攔,可是考慮的要素比較多,并且作用不是太好,故拋棄這種辦法。
2).批改某些辦法,進(jìn)入到上圖if判別中
在滑動橫向RecyclerView 到兩頭時,dx != 0 && !isGutterDrag(mLastMotionX, dx) 必定滿意條件,那闡明canScroll()(用來判別一個View以及它的子View是否能夠滑動)必定回來了false, 復(fù)寫canScroll()辦法,打log,發(fā)現(xiàn)回來公然為false,驗證了自己的判別。
處理辦法:復(fù)寫canScroll,當(dāng)View 是橫向RecyclerView(LinearLayoutManager 包括GridLayoutManager)時,直接回來true即可處理問題,處理代碼如下:
相似的抵觸還有ViewPager 和HorizontalScrollView 等等,處理辦法與上面相似。
2.縱向RecyclerView/ListView 與 橫向RecyclerView 之間的滑動抵觸
在有些時分因為產(chǎn)品需求,需求在縱向的RecyclerView/ListView內(nèi)嵌套一個橫向的RecyclerView,當(dāng)這個橫向RecyclerView的item 比高度較大的時分(企鵝FM書城排行榜模塊),在橫向滑動時,簡略導(dǎo)致全體向上滑,體會作用較差,如下圖所示(網(wǎng)絡(luò)圖) :
形成上述現(xiàn)象的原因是:外層縱向滑動的RecyclerView對 橫向滑動的RecyclerView 的滑動事情進(jìn)行了阻攔,如下圖2 所示,canScrollVertically 此刻為true,因而這兒僅僅只判別了Math.abs(dy)>mTouchSlop(能夠認(rèn)為是一個滑動閥值,是一個定值8dp) ,并未判別方向或視點,然后決議是否阻攔。
處理辦法 :
已然RecyclerView::onInterceptTouchEvent 內(nèi)部沒有判別滑動視點或方向,那咱們就人為去判別,在上面判讀的基礎(chǔ)上持續(xù)判別 Math.abs(dy) 和Math.abs(dx) 的巨細(xì),然后決議是否阻攔:詳細(xì)剖析細(xì)節(jié)可參照此地址:
https://www.bbsmax.com/A/pRdBnnYadn/
運(yùn)用上述辦法,能夠很快處理上述滑動體會問題,那是不是只要上述一種處理辦法了,答案是否定的,作為一名Android 開發(fā)者咱們知道,除了上述辦法阻攔滑動事情外,咱們還能夠經(jīng)過getParent().requestDisallowInterceptTouchEvent(true); 讓父RecyclerView不去阻攔橫向滑動,如下是RecyclerView::onTouchEvent() ,內(nèi)部現(xiàn)已完成了requestDisallowInterceptTouchEvent(true) 。
咱們需求考慮的是,當(dāng)咱們橫向上或橫向下滑動時,需求 進(jìn)入上圖中1的判別 ,2的判別還未滿意,此刻內(nèi)部橫向RecyclerView 會阻攔內(nèi)部itemView的滑動事情,進(jìn)而履行自己的onTouchEvent事情,然后調(diào)用requestDisallowInterceptTouchEvent(true) ,讓外層RecyclerView不去阻攔內(nèi)部RecyclerView的橫向滑動事情,至此需求處理怎么確保先進(jìn)入1判別而不進(jìn)入2判別。
處理辦法:經(jīng)過調(diào)整TouchSlop值的巨細(xì)
在開端咱們已介紹RecyclerView 的默許TouchSlop 值是8dp,假如要先確保進(jìn)入1判別條件,有必要調(diào)大TouchSlop值(反射獲?。?,經(jīng)過調(diào)整TouchSlop (按倍數(shù)調(diào)整比較簡略,能夠先知道一個大致規(guī)模)驗證,當(dāng)TouchSlop擴(kuò)展1倍時就能滿意條件。
總結(jié):上述兩種辦法各有優(yōu)缺陷,辦法1,對原生RecyclerView 侵入性較強(qiáng)(特別是對RecyclerView 進(jìn)行多層封裝的狀況下,影響比較大),長處是TouchSlop 值堅持與體系共同,不會帶來其他不知道問題;辦法 2 ,批改辦法簡略,侵略性小,缺陷,需求調(diào)整TouchSlop 值,可能還會帶來其他問題。
3.橫向RecyclerView ItemView 滑動不斷留在中心態(tài)
如下圖所示,正在滑動的模塊是書城——排行榜模塊,排行榜模塊首要由橫向RecyclerView 構(gòu)成,內(nèi)部包括兩個榜單辦法,羅列前top3的內(nèi)容,在(2)的基礎(chǔ)上處理了縱向RecyclerView 嵌套橫向RecyclerView 滑動問題外,還有有個小問題那就是,RecyclerView ItemView 滑動多少就停在那里,這種作用不是咱們想要的,咱們想要的是滑到左面就顯現(xiàn)第一個榜單,滑到右邊就顯現(xiàn)第二個榜單。
那有沒有好的辦法做到這一點了,官方考慮到這一點,針對RecyclerView 滑動狀況,詳細(xì)介紹能夠自己去查一查,運(yùn)用適當(dāng)簡略,針對上述問題處理辦法如下:
4.記載、康復(fù)RecyclerView 翻滾偏移方位
了解RecyclerView 緩存的同學(xué)應(yīng)該知道(后邊在也會介紹RecyclerView緩存機(jī)制),當(dāng)RecyclerView中的itemView 滑出屏幕后會緩存在mCacheView 中(默許緩存最大數(shù)是2),因而當(dāng)滑出屏幕超越2后,再滑回來,本來的方位信息都會被重置,關(guān)于一般的RecyclerView 沒有什么影響,可是假如內(nèi)嵌了一個橫向RecyclerView (如下圖中分類模塊方位) ,起先”懸疑推理“ 在一排第一個方位,向左滑動到其他方位后,再縱向滑動外層RecyclerView ,發(fā)現(xiàn)分類模塊第一個又變成了”懸疑推理“ ,這個是產(chǎn)品不能承受的。
那怎么批改上述問題了,RecyclerView 布局 及方位相關(guān)信息都是由對應(yīng)LayoutManager決議,因而檢查對應(yīng)LayoutManager::onSaveInstanceState() 如下所示,內(nèi)部的確記載了position及offset 值。
處理辦法過程:
(1).在Adapter::onViewRecycled 中保存對應(yīng)LayoutManager的onSaveInstanceState ,一起記載保存下來
(2).在setData()數(shù)據(jù)給Adapter 時,康復(fù)對應(yīng)LayoutManager 之前保存在數(shù)據(jù)信息
(3).保存記載RecyclerView 后的作用
RecycleView入坑
1.RecyclerView 導(dǎo)致的內(nèi)存走漏(support 26 + 7.0以下機(jī)型)
在進(jìn)行4.0 版別迭代時,發(fā)現(xiàn)在之前的播送聚合頁存在RecyclerView導(dǎo)致的內(nèi)存走漏,下圖為內(nèi)存走漏的引證鏈,引證目標(biāo)能夠追到GapWorker。這兒的RecyclerView是一個橫向的RecyclerView ,作為播送聚合頁(ListView)的HeaderView。
因為播送頁面是比較老的頁面,最近幾個版別也未發(fā)現(xiàn)此類走漏,細(xì)細(xì)想一下,可能與RecyclerView 版別有關(guān)(4.0版別直接將support 庫由23.1升級到26.1版別),剛好這幾個版別,support 庫 批改了批改許多RecyclerView 的bug 及添加了許多新功能。經(jīng)過AndroidXRef 查詢知(查詢成果如下),GapWorker 公然是在support 26 新增的。
檢查GapWorker ,里邊sGapWorker 是一個ThreadLocal 帶GapWorker 的目標(biāo),一起保持了一個RecyclerView 的List目標(biāo)(經(jīng)過add 和remove 辦法進(jìn)行)。
而GapWorker的add 和remove 辦法分別在RecyclerView::onAttachedToWindow 和RecyclerView::onDetachedFromWindow 中調(diào)用,如下圖所示:
依據(jù)上面的引證鏈知,RecyclerView::onDetachedFromWindow 辦法 沒有被自動調(diào)用,斷點驗證,在退出播送頁面的時分也沒有調(diào)用(導(dǎo)致走漏),按理說在滑動離屏的時分就應(yīng)該調(diào)用的,莫非和RecylerView 做為ListView 的HeaderView 有關(guān),順著這條思路發(fā)現(xiàn)公然和上述運(yùn)用辦法有關(guān)。
之前遇到過:ListView 嵌套GridView時,GridView數(shù)據(jù)紊亂問題(7.0及其以上有問題),里邊剛好闡明晰7.0及其以上版別,官方批改了RecylerView 做為ListView 的HeaderView 狀況,滑出屏幕,不調(diào)用onDetachedFromWindow()的原因,詳細(xì)如下:
從剖析中,能夠獲取到兩個重要的信息:
GapWorker 是在support 26 以上才有的,且SDK_INT>=21,才會進(jìn)行對應(yīng)add 和remove 操作 ;
在SDK_INT< 24(7.0) 時,不會自動調(diào)用View::dispatchDetachedFromWindow()。
因而,上述問題的處理辦是:在對應(yīng)Fragment 的onDetach() 或 其他場景首要去調(diào)用上圖中的ViewGroup::removeDetachedView() (這兒需求運(yùn)用反射),詳細(xì)如下:
2.RecyclerView調(diào)用notifyDataSetChanged 會閃耀
直接看此文章就能夠了,地址為:
https://www.jianshu.com/p/29352def27e6
3.RecycleView /ListView 設(shè)置itemView 為View.GONE 作用等同于View.Invisible
處理辦法:
將itemView 的寬高設(shè)置成 0 ,從頭設(shè)置一下LayoutParams