1. RecycleView 如何设置分割线的?
它是读取系统自带的属性
R.attr.listDivider属性,来设置分割线的,支持横向和纵向。
2. RecycleView 分割线的样式如何修改?
第一种办法:因为
R.attr.listDivider定义在系统样式中,那么我们可以重写这个属性修改,如下:在
styles.xml中添加该属性:1
2
3
4
5<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
....
<item name="android:listDivider">@drawable/drawable_bg</item>
</style>其中
drawable_bg为自定义分割线的drawable.第二种办法:可以通过自带的
setDrawable()改变,如下:1
2
3
4DividerItemDecoration itemDecoration = new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);
Drawable drawable = getResources().getDrawable(R.drawable.drawable_bg);
itemDecoration.setDrawable(drawable);
recyclerView.addItemDecoration(itemDecoration);3. RecycleView 缓存
缓存涉及三个类,分别是:
RecyclerRecycledViewPoolViewCachedExtension
Recycler: 用于管理已经废弃或者与RecyclerView分离的ViewHoldermChangedScrap: 与RecyclerView分离的ViewHolder列表mAttachedScrap: 未与RecyclerView分离的ViewHolder列表mCachedViews:ViewHolder缓存列表
RecycledViewPool:ViewHolder缓存池ViewCachedExtension: 开发者可以控制的ViewHolder缓存的帮助类
获取缓存里的 ViewHolder 的关键方法是: tryGetViewHolderForPositionByDeadline 如下:
1 |
|
总结缓存的机制如下:
- 1.从
mChangedScrap或mAttachScrap中获取 - 2.从
mCacheViews - 3.从
mRecycledViewPool缓存池中获取
4. RecycleView 刷新闪屏
一般刷新:
1 | adapter.notifyItemChanged(0); 参数:position |
实际上会全部刷新,局部刷新应使用:
1 | adapter.notifyItemChanged(0,"q"); 参数: position,payload |
并且在 adapter 中重写方法: `onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads)方法`
payloads 的 size 始终为1.
1 |
|
5. RecycleView 和 listView
//https://zhuanlan.zhihu.com/p/23339185
缓存层级不同
recycleView 比 listView 多两级缓存,支持开发者自定义缓存处理,支持多个recycleView共用同一个 recycleViewPool(缓存池)
listView 缓存(两级缓存):
| 是否需要回调createView | 是否需要回调bindVIew | 生命周期 | 备注 | |
|---|---|---|---|---|
| mActiveViews | 否 | 否 | onLayout函数周期内 | 用于屏幕内itemView快速重用 |
| mScrapViews | 否 | 是 | 与mAdapter一致,当Adapter被更换时,mScrapViews即被清空 |
RecycrleView 缓存( 四级缓存):
| 是否回调createView | 回调bindVIew | 生命周期 | 备注 | |
|---|---|---|---|---|
| mAttachedScrap | 否 | 否 | onLayout | 用于屏幕内itemView快速重用 |
| mCacheViews | 否 | 否 | 与mAdapter一致,当Adapter被更换时,mCacheVIews被缓存到mRecyclerPool | 默认缓存大小是2 |
| mViewCacheExtension | 用户自定义实现 | |||
| mRecyclerPool | 否 | 是 | 与自身生命周期一致,不再引用时被释放 | 默认大小是5 |
ListView 和 RecyclerView 缓存机制:
mActiveViews和mAttachedScrap功能相似,意义在于快速重用屏幕上可见的列表项itemView,而不需要重新createView和bindViewmScrapView和mCachedViews+mRecyclerViewPool功能相似,意义在于缓存离开屏幕的itemView,目的是让即将进入屏幕的itemView重用RecycleView的优势在于mCacheVIews的使用,可以做到屏幕外的列表项itemView进入屏幕内时也无须bindView快速重用;mRecycleViewPool可以供多个recycleView共同使用,在特定场景下,如viewpager加多个列表页有优势。
6. RecycleView 的回收复用机制的内部实现都是哪个类完成的?
RecycleView 的回收复用机制都是由内部类
Recycler类,核心方法:tryGetViewHolderForPositionByDeadline()方法中完成的。
随笔源码记录:
复用机制
1 | public View getViewForPosition(int position) { |
回收机制
也是 Recycler 类中的,核心方法:
recycleViewHolderInternal(holder)
1 | public void recycleView(View view) { |
回收的逻辑:由 LayoutManager 来遍历移出屏幕的卡位,然后对每个卡位进行回收操作,回收时,都是把 viewHolder 放在 mCachedViews 里面,如果mCachedViews 满了,那就在mCachedViews 里拿一个ViewHolder 扔到 ViewPool 缓存里,然后mCachedViews 就可以空出位置来放新回收的ViewHolder 了。
mCachedViews里存放的ViewHolder只有原本位置的卡位才能复用参考链接:https://blog.csdn.net/xJ032w2j4cCjhOW8s8/article/details/79946425
===================================================================================
updateViewCacheSize()
1 | void updateViewCacheSize() { |
在第一次向上滑动
7. RecycleView 性能优化
7.1 数据处理和视图加载适度分离
什么意思呢?就是我们异步从服务端拉取数据,拿到数据之后就把数据丢给
ViewHolder进行处理,当然有些数据需要单独处理,那么其实这些处理逻辑也应该异步执行,这样的话就可以让adapter在 notifyDatasetChanged 之后,ViewHolder 就可以简单处理视图与数据的绑定工作。
7.2 数据刷新
如果是个别数据发生改变,调用局部刷新的方法
7.3 布局优化
减少 GPU 过度绘制;
7.4 杂七杂八
- 升级 RecycleView 版本到 25.1.0 以上使用 Prefetch 功能,开启这个功能之后对页面复杂度的敏感性降低,在高复杂度情况下,Prefetch 可以显著提高页面流畅度。Android 5.0 以后引入
Render Thread提升动画流畅性。- 如果item 高度固定的话,可以使用
RecyclerView.setHasFixSize(true)来避免requestLayout浪费资源;- 共用 RecycleViewPool ;
- 通过 getExtraLayoutSpace 来增加 RecyclerView 预留的额外空间,默认是一页数据的数量
8. 自定义属性获取完属性后为什么调用TypeArray.recycle()?
1 | /** |
1 | public class Resources{ |
看上面的那个大概意思就是从一个同步池子里获取到的,默认大小是5. 平常我们获取的属性是通过:
1 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.xx); |
context 的一个静态方法,点进去看看:
1 | //最终到了这里 |
SynchronizedPool
1 | /** |
维护一个同步栈结构的对象池,避免重复频繁创建属性值 TypeArray 对象,使用时记得调用 Recycle() 方法将不用的对象返回至对象池来达到重用的目的,如果不调用 recycle() 方法将会随着 Activity的每一次创建而创建,因此系统频繁创建 array, 对内存和性能是一个不小的开销,如果不使用池,每次都让 GC 来回收,很可能就会造成 OOM.
9. 自定义属性
1 | //自定义属性一般我们会在values-attr.xml 中定义,如下: |
两种方式有什么区别呢?
第一种方式会把有关这个控件的属性包裹起来,达到一个分组的概念,不零散,属性的使用范围更加明确,
第一种方式获取属性值:
1 | TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.customerView); |
第二种方式获取属性值:
1 | int[] array = {R.attr.AloneAttr}; |