您的位置:首页 >  新闻中心 > 开发者专区
  开发者专区
 

RecyclerView 体验优化及入坑总结

来源:原创    时间:2018-01-29    浏览:0 次

前语

本文所讲RecyclerView 是来自support 库 26 版别,本文首要来源于本身开发及组内搭档遇到问题的经验总结,作为常识沉积记载一下,以备日后检查。
本文首要解说以下几部分:
1.RecyclerView 滑动体会篇

横向ViewPager与内嵌横向RecyclerView之间的滑动抵触;
纵向RecycleView/ListView与横向RecycleView之间的滑动抵触;
横向RecyclerView ItemView滑动不断留在中心态;
记载、康复RecyclerView翻滚偏移方位;
2.RecyclerView 入坑篇

RecyclerView导致的内存走漏(support 26 + 7.0以下机型);
RecyclerView调用notifyDataSetChanged 会闪耀;
RecycleView/ListView设置itemView 为View.GONE 作用等同于View.Invisible;
RecycleView滑动体会

1.ViewPager与横向RecyclerView之间的滑动抵触

现在,企鹅FM项目中,许多页面运用ViewPager+ TabLayout (如主页、详情页、查找成果页等),而对应页面许多时分会嵌套一个横向RecycleView,用来展示更多的信息,如下,在RecycleView中滑动到最终一个元素时,会一起带动ViewPager滑动,这种体会极差。
blob.png
原因剖析:

作为子View 的RecyclerView在滑到最终一个或第一个ItemView到导致ViewPager滑动,这必定是ViewPager在此刻对滑动事情进行了阻拦,处理的最简略办法就是不让ViewPager阻拦横向RecyclerView的滑动事件(即ViewPager::onInterceptTouchEvent办法回来false),ViewPager::onInterceptTouchEvent中的Move 事件如下:
blob.png
现在,有以下两种办法使ViewPager 不去阻拦横向RecyclerView 滑动事情:

1).在RecyclerView 对应滑动事情分发中调用        

getParent().requestDisallowInterceptTouchEvent(true); 阻挠ViewPager对其MOVE或许UP事情进行阻拦,可是考虑的要素比较多,并且作用不是太好,故抛弃这种办法。

2).批改某些办法,进入到上图if判别中

在滑动横向RecyclerView 到两头时,dx != 0 && !isGutterDrag(mLastMotionX, dx) 必定满意条件,那阐明canScroll()(用来判别一个View以及它的子View是否能够滑动)必定回来了false, 复写canScroll()办法,打log,发现回来公然为false,验证了自己的判别。

处理办法:复写canScroll,当View 是横向RecyclerView(LinearLayoutManager 包括GridLayoutManager)时,直接回来true即可处理问题,处理代码如下:


相似的抵触还有ViewPager 和HorizontalScrollView 等等,处理办法与上面相似。 

2.纵向RecyclerView/ListView 与 横向RecyclerView 之间的滑动抵触

在有些时分因为产品需求,需求在纵向的RecyclerView/ListView内嵌套一个横向的RecyclerView,当这个横向RecyclerView的item 比高度较大的时分(企鹅FM书城排行榜模块),在横向滑动时,简略导致全体向上滑,体会作用较差,如下图所示(网络图) :



形成上述现象的原因是:外层纵向滑动的RecyclerView对 横向滑动的RecyclerView 的滑动事情进行了阻拦,如下图2 所示,canScrollVertically 此刻为true,因而这儿仅仅只判别了Math.abs(dy)>mTouchSlop(能够认为是一个滑动阀值,是一个定值8dp) ,并未判别方向或视点,然后决议是否阻拦。


处理办法 :

已然RecyclerView::onInterceptTouchEvent 内部没有判别滑动视点或方向,那咱们就人为去判别,在上面判读的基础上持续判别 Math.abs(dy) 和Math.abs(dx) 的巨细,然后决议是否阻拦:详细剖析细节可参照此地址:

https://www.bbsmax.com/A/pRdBnnYadn/

运用上述办法,能够很快处理上述滑动体会问题,那是不是只要上述一种处理办法了,答案是否定的,作为一名Android 开发者咱们知道,除了上述办法阻拦滑动事情外,咱们还能够经过getParent().requestDisallowInterceptTouchEvent(true); 让父RecyclerView不去阻拦横向滑动,如下是RecyclerView::onTouchEvent() ,内部现已完成了requestDisallowInterceptTouchEvent(true) 。

咱们需求考虑的是,当咱们横向上或横向下滑动时,需求 进入上图中1的判别 ,2的判别还未满意,此刻内部横向RecyclerView 会阻拦内部itemView的滑动事情,进而履行自己的onTouchEvent事情,然后调用requestDisallowInterceptTouchEvent(true) ,让外层RecyclerView不去阻拦内部RecyclerView的横向滑动事情,至此需求处理怎么确保先进入1判别而不进入2判别。


处理办法:经过调整TouchSlop值的巨细 

在开端咱们已介绍RecyclerView 的默许TouchSlop 值是8dp,假如要先确保进入1判别条件,有必要调大TouchSlop值(反射获取),经过调整TouchSlop (按倍数调整比较简略,能够先知道一个大致规模)验证,当TouchSlop扩展1倍时就能满意条件。

总结:上述两种办法各有优缺陷,办法1,对原生RecyclerView 侵入性较强(特别是对RecyclerView 进行多层封装的状况下,影响比较大),长处是TouchSlop 值坚持与体系共同,不会带来其他不知道问题;办法 2 ,批改办法简略,侵略性小,缺陷,需求调整TouchSlop 值,可能还会带来其他问题。


3.横向RecyclerView  ItemView 滑动不断留在中心态

如下图所示,正在滑动的模块是书城——排行榜模块,排行榜模块首要由横向RecyclerView 构成,内部包括两个榜单办法,罗列前top3的内容,在(2)的基础上处理了纵向RecyclerView 嵌套横向RecyclerView 滑动问题外,还有有个小问题那就是,RecyclerView  ItemView 滑动多少就停在那里,这种作用不是咱们想要的,咱们想要的是滑到左面就显现第一个榜单,滑到右边就显现第二个榜单。


那有没有好的办法做到这一点了,官方考虑到这一点,针对RecyclerView 滑动状况,详细介绍能够自己去查一查,运用适当简略,针对上述问题处理办法如下:



4.记载、康复RecyclerView 翻滚偏移方位

了解RecyclerView 缓存的同学应该知道(后边在也会介绍RecyclerView缓存机制),当RecyclerView中的itemView 滑出屏幕后会缓存在mCacheView 中(默许缓存最大数是2),因而当滑出屏幕超越2后,再滑回来,本来的方位信息都会被重置,关于一般的RecyclerView 没有什么影响,可是假如内嵌了一个横向RecyclerView (如下图中分类模块方位) ,起先”悬疑推理“ 在一排第一个方位,向左滑动到其他方位后,再纵向滑动外层RecyclerView ,发现分类模块第一个又变成了”悬疑推理“ ,这个是产品不能承受的。


那怎么批改上述问题了,RecyclerView 布局 及方位相关信息都是由对应LayoutManager决议,因而检查对应LayoutManager::onSaveInstanceState() 如下所示,内部的确记载了position及offset 值。


处理办法过程:

(1).在Adapter::onViewRecycled 中保存对应LayoutManager的onSaveInstanceState ,一起记载保存下来



(2).在setData()数据给Adapter 时,康复对应LayoutManager 之前保存在数据信息
(3).保存记载RecyclerView 后的作用


RecycleView入坑

1.RecyclerView 导致的内存走漏(support 26 + 7.0以下机型)

在进行4.0 版别迭代时,发现在之前的播送聚合页存在RecyclerView导致的内存走漏,下图为内存走漏的引证链,引证目标能够追到GapWorker。这儿的RecyclerView是一个横向的RecyclerView ,作为播送聚合页(ListView)的HeaderView。



因为播送页面是比较老的页面,最近几个版别也未发现此类走漏,细细想一下,可能与RecyclerView 版别有关(4.0版别直接将support 库由23.1升级到26.1版别),刚好这几个版别,support 库 批改了批改许多RecyclerView 的bug 及添加了许多新功能。经过AndroidXRef 查询知(查询成果如下),GapWorker 公然是在support 26 新增的。


检查GapWorker ,里边sGapWorker 是一个ThreadLocal 带GapWorker 的目标,一起保持了一个RecyclerView 的List目标(经过add  和remove 办法进行)。



而GapWorker的add 和remove 办法分别在RecyclerView::onAttachedToWindow 和RecyclerView::onDetachedFromWindow 中调用,如下图所示:



依据上面的引证链知,RecyclerView::onDetachedFromWindow 办法 没有被自动调用,断点验证,在退出播送页面的时分也没有调用(导致走漏),按理说在滑动离屏的时分就应该调用的,莫非和RecylerView 做为ListView 的HeaderView 有关,顺着这条思路发现公然和上述运用办法有关。

之前遇到过:ListView 嵌套GridView时,GridView数据紊乱问题(7.0及其以上有问题),里边刚好阐明晰7.0及其以上版别,官方批改了RecylerView 做为ListView 的HeaderView 状况,滑出屏幕,不调用onDetachedFromWindow()的原因,详细如下:





从剖析中,能够获取到两个重要的信息:

GapWorker 是在support 26 以上才有的,且SDK_INT>=21,才会进行对应add 和remove 操作 ;

在SDK_INT< 24(7.0) 时,不会自动调用View::dispatchDetachedFromWindow()。

因而,上述问题的处理办是:在对应Fragment 的onDetach() 或 其他场景首要去调用上图中的ViewGroup::removeDetachedView()  (这儿需求运用反射),详细如下:
blob.png

2.RecyclerView调用notifyDataSetChanged 会闪耀

直接看此文章就能够了,地址为:

https://www.jianshu.com/p/29352def27e6

3.RecycleView /ListView 设置itemView 为View.GONE 作用等同于View.Invisible

 处理办法:

将itemView 的宽高设置成 0 ,从头设置一下LayoutParams