2019-08-07:说说你对RecyclerView的了解,是否了解其缓存的内部机制?
RecyclerView 是什么?
RecyclerView是Android5.0推出,Google工程师在support-v7包中引入的一个全新列表控件,用于显示庞大数据集容器,可通过保持有限数量的视图进行非常有效的滚动操作,它不关心item是否显示在正确的位置以及如何显示,通过LayoutManager控制布局是横向还是纵向,它不关心 item 如何 分隔,通过 ItemDecoration 来绘制分割线,它不关心 item 增加或删除的动画效果,你可以通过 ItemAnimation 绘制你想要的动画效果,RecyclerView仅仅关注如何回收和复用view,如果有数据集合,其中元素将因用户操作或网络事件而发生改变,建议使用RecyclerView
RecyclerView使用
一.添加依赖
使用RecyclerView需要在 app/build.gradle添加 相关依赖,然后同步一下就可以使用依赖了:
implementation 'com.android.support:recyclerview-v7:25.3.1'
二.编写代码
创建布局文件
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
创建完成后在Activity获取RecycleView对象,并声明 LayoutManager 与 Adapter,代码如下:
public class MainActivity extends AppCompatActivity implements SimpleAdapter.OnItemClickListener {
private RecyclerView mRecyclerView;
private List<String> mDatas;
private SimpleAdapter mAmAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initDatas();
initView();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
final int itemId = item.getItemId();
switch (itemId) {
case R.id.action_add:
mAmAdapter.addData(1);
mRecyclerView.setAdapter(mAmAdapter);
break;
case R.id.action_delete:
mAmAdapter.deleteData(1);
mRecyclerView.setAdapter(mAmAdapter);
break;
case R.id.action_grid_view:
mRecyclerView.setAdapter(new SimpleAdapter(this, mDatas, false));
mRecyclerView.setLayoutManager(new GridLayoutManager(this, 5));
break;
case R.id.action_list_view:
mRecyclerView.setAdapter(new SimpleAdapter(this, mDatas, true));
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
break;
case R.id.action_hor_grid_view:
mRecyclerView.setAdapter(new SimpleAdapter(this, mDatas, false));
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(5, StaggeredGridLayoutManager.HORIZONTAL));
break;
case R.id.action_stagger_view:
startActivity(new Intent(this, StaggeredGridViewActivity.class));
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
private void initView() {
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
mAmAdapter = new SimpleAdapter(this, mDatas, true);
// 设置RecyclerView的布局管理
mRecyclerView.setAdapter(mAmAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));
mAmAdapter.setOnItemClickListener(this);
}
private void initDatas() {
mDatas = new ArrayList<>();
for (int i = 'A'; i < 'z'; i++) {
mDatas.add("" + (char) i);
}
}
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this, "Click:" + position
, Toast.LENGTH_LONG).show();
}
@Override
public void onItemLongClick(View view, int position) {
mAmAdapter.deleteData(position);
}
}
RecycleView 和 ListView 一样需要适配器的,但是 RecycleView 需要设置 布局管理器(setLayoutManager),这是为了方便扩展,这里使用了 LinearLayoutManager.其中 Adapter 的创建比较关键,来看一下 SimpleAdapter 的代码:
public class SimpleAdapter extends RecyclerView.Adapter<MyViewHolder> {
public LayoutInflater mInflater;
private Context mContext;
private List<String> mDatas;
private boolean mIsListView;
public OnItemClickListener mListener;
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.mListener = listener;
}
public SimpleAdapter(Context context, List<String> datas, boolean isListView) {
this.mContext = context;
this.mDatas = datas;
this.mInflater = LayoutInflater.from(context);
this.mIsListView = isListView;
}
public void addData(int position) {
if (position >= 1) {
mDatas.add(mDatas.get(position));
notifyItemInserted(position);
}
}
public void deleteData(int postion) {
mDatas.remove(postion);
notifyItemRemoved(postion);
}
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
final View view = mInflater.inflate(mIsListView ? R.layout.item_simple_list_view : R.layout.item_simple_text_view, parent, false);
MyViewHolder viewHolder = new MyViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull final MyViewHolder holder, final int position) {
holder.tv.setText(mDatas.get(position));
setUpItemEvent(holder);
}
public void setUpItemEvent(@NonNull final MyViewHolder holder) {
if (mListener != null) {
final int layoutPosition = holder.getLayoutPosition();
holder.tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mListener.onItemClick(holder.tv, layoutPosition);
}
});
holder.tv.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
mListener.onItemLongClick(holder.tv, layoutPosition);
return false;
}
});
}
}
@Override
public int getItemCount() {
return mDatas.size();
}
}
上面用到 item_simple_text_view.xml 布局文件,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:background="@color/colorAccent"
android:layout_margin="3dp"
android:id="@+id/layout_frame"
android:layout_height="wrap_content">
<TextView
android:id="@+id/id_tv"
android:gravity="center"
android:layout_width="72dp"
android:layout_height="72dp"/>
</FrameLayout>
SimpleAdapter 继承 RecyclerView.Adapter ,需要一个ViewHolder 泛型,创建 ViewHolder 需要继承 RecycleView.ViewHolder , ViewHolder 的构造方法需要传递 View 对象, View 对象 需要继承 RecycleView.ViewHolder,ViewHolder 构造方法需要传递 View对象,View对象会和 ViewHolder 进行绑定.
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.id_tv);
}
}
可以发现 RecyclerView.Adapter 设计针对性很强,强制程序员通过ViewHolder 和 复用 View 进行优化,不会出现之前 ListView 不采取 优化情况.这种机制避免创建过多View和频繁调用findViewById的方法
三.运行
写完这些代码后,这个simple就可以运行起来了,从例子也可以看出,RecycleView的用法并不比 ListView 复杂,反而更加灵活好用,它将数据,排列方式,数据展示方式都分割开来,因此自定义的形式也非常多,非常灵活
RecyclerView不同布局的排列方式
上面的效果是水平列表,还可以选择其他排列方式,非常灵活,这就是比单一的listView/GridView强大的地方
- ListView
以垂直列表方式展示显示项目
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
- GrideView
以垂直列表方式展示显示表格布局
mRecyclerView.setLayoutManager(new GridLayoutManager(this, 5));
- 横向 ListView
如果想设置一个横向的列表,只要设置 LinearLayoutManager 就行,注意, 要声明 LayoutManager 的类型是 LinearLayoutManager ,而不是父类的 LayoutManager
mRecyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));
- 横向 GrideView
以水平列表方式展示显示表格布局
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(5, StaggeredGridLayoutManager.HORIZONTAL))```
- 瀑布流
一.高度绘制
for (int i = 0; i < mDatas.size(); i++) {
mHeight.add((int) (100 + Math.random() * 300));
}
二.布局绘制
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
layoutParams.height = mHeight.get(position);
holder.itemView.setLayoutParams(layoutParams);
holder.tv.setText(mDatas.get(position));
}
三.设置RecyclerView的布局管理
StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(manager);
四.RecyclerView添加点击事件
当使用一段时间的RecycleView,发现为其实现每一项点击事件并没有ListView那么轻松,ListView直接加一个 OnItemClickListerner 即可,实际上我们不要把 RecyclerView 当作 ListView使用,希望大家把他看成一个容器,里面包含不同的item,它们可以设置不同方式的排列组合,非常灵活,点击事件按照自己的意愿进行实现.那么如何为RecycleView添加点击事件呢? 其实我发现,Adapter是添加点击事件一个很好的地方,里面构造布局等View的主要场所,也是布局和数据进行绑定的地方,首先在 Adapter 中创建一个实现点击接口,其中View是点击 item,data是数据,postion是条目位置,因为我们想知道点击的区域部分的数据和位置是什么,以便下一步进行操作:
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
定义完接口,在 Adapter 中添加 要实现的接口和添加设置的方法
public OnItemClickListener mListener;
public void setOnItemClickListener(OnItemClickListener listener) {
this.mListener = listener;
}
那么这个接口用在什么地方呢?如下代码,为Adapter 实现 onClickListerner 方法:
@Override
public void onBindViewHolder(@NonNull final MyViewHolder holder, final int position) {
holder.tv.setText(mDatas.get(position));
setUpItemEvent(holder);
}
public void setUpItemEvent(@NonNull final MyViewHolder holder) {
if (mListener != null) {
final int layoutPosition = holder.getLayoutPosition();
holder.tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mListener.onItemClick(holder.tv, layoutPosition);
}
});
holder.tv.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
mListener.onItemLongClick(holder.tv, layoutPosition);
return false;
}
});
}
}
做完这些事情就可以在 Activity 或者其他地方 为 RecycleView 添加项目点击事件了,如:
private void initView() {
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
mAmAdapter = new SimpleAdapter(this, mDatas, true);
// 设置RecyclerView的布局管理
mRecyclerView.setAdapter(mAmAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));
mAmAdapter.setOnItemClickListener(this);
}
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this, "Click:" + position
, Toast.LENGTH_LONG).show();
}
@Override
public void onItemLongClick(View view, int position) {
mAmAdapter.deleteData(position);
}
五.RecycleView添加删除数据 以前在ListView中,只要 修改数据后用 Adapter 的 notifyDatasetChange 一下就可以更新界面.然而,在RecycleView 中还有高级的用法,可以让添加或者删除条目自带动画,可以在SimpleAdapter 传创建 addItem 和 removeItem 方法.
添加数据:
public void addData(int position) {
if (position >= 1) {
mDatas.add(mDatas.get(position));
notifyItemInserted(position);
}
}
删除数据:
public void deleteData(int postion) {
mDatas.remove(postion);
notifyItemRemoved(postion);
}
注意,这里更新数据集不是 adapter.notifyDataSetChange(),而是 notifyItemInserted(postion)与 notifyItemRemoved(postion),否则没有动画效果
RecyclerView核心类
-
Adapter 使用RecyleView之前需要继承 自 RecyleView.Adapter 的适配器,作用是将适配器与每一个 item 界面进行绑定
-
ViewHolder
- viewHolder 和 item view 是什么关系?一对一?一对多?还是多对一?
-
view Holder 解决的是什么问题?
-
view Holder 的 Listview item view 复用有什么关系?
没有实现 view Holder 的 getView() 会重复执行 findViewById,findViewById 底层实现是一个 dns ,深度优先复杂度,所以viewHolder模式实现了getView方法,他的来历是用来保存View的convertView
不实现 view Holder 还会复用 item view 吗?这个问题我还有待研究.
-
LayoutManager LayoutManager 用来确定 每一个 item 如何排列摆放,何时展示和隐藏.回收或重用一个View时,LayoutManager会向适配器请求的数据替换旧的数据,这种机制避免创建过多的View和频繁调用findViewById的方法
-
ItemDecoration
-
ItemAnimation

RecyclerView与ListView相比的优势
因为ListView只有纵向列表一种布局,不像RecyleView一样支持 Linear, Grild ,Stagged,Stagged ,Grid 各种可扩展布局,没有支持动画的API,但是RecyleView可以通过ItemAnimation自定义你想要的动画,相关监听接口如:setOnClickListerner(),setOnLongItenListerner(),setSelection()的设计和系统也不一致,并且没有强制实现ViewHolder,RecyleView做到了这一点,降低了耦合,另外ListView性能也不如RecyclerView,所以强烈推荐大家使用RecyclerView
RecyclerView 滑动场景下的回收复用涉及到的结构体两个: mCachedViews 和 RecyclerViewPool
mCachedViews 优先级高于 RecyclerViewPool,回收时,最新的 ViewHolder 都是往 mCachedViews 里放,如果它满了,那就移出一个扔到 ViewPool 里好空出位置来缓存最新的 ViewHolder。
复用时,也是先到 mCachedViews 里找 ViewHolder,但需要各种匹配条件,概括一下就是只有原来位置的卡位可以复用存在 mCachedViews 里的 ViewHolder,如果 mCachedViews 里没有,那么才去 ViewPool 里找。
在 ViewPool 里的 ViewHolder 都是跟全新的 ViewHolder 一样,只要 type 一样,有找到,就可以拿出来复用,重新绑定下数据即可。
RecyclerView性能优化策阅
本内容参考 今日头条 Blankj 阿里巴巴面经
-
数据优化
-
服务端获取数据分页数据的时候进行缓存,提升二次加载的速度
-
对于新增或删除的数据 使用 DiffUtis 来进行局部刷新,而不是全局刷新
-
尽量多使用notifyInsertChangeSet() 而不是 notifyChangeSet()
-
布局优化
-
减少过渡绘制
-
避免使用ConstrainLayout ,可以使用ReativeLayout 减少嵌套层级,用Layout Inspector 查看 View 层级
-
减少布局层级
-
-
减少 xml 文件 inflate 时间
- xml Inflate 出 itemView是通过耗时 I/O 操作的,尤其是 type 复用率 低的情况下,这种inflate 方式的损耗极大,所以我们不要考虑用inflate了,可以考虑用代码绘制,类似于 new inflate()
-
其他
-
item 高度固定,设置RecyclerView.setHasFixedSize(true),避免requeLayout 资源浪费
-
设置 RecyclerView.addOnScrollListener(listener); 来对滑动过程中停止加载操作
-
如果不要求动画,可用通过 ((SimpleItemAnimator) rv.getItemAnimator()).setSupportsChangeAnimations(false); 把默认动画关闭来提高性能
-
TextView 使用 String.toUpperCase 替换 android:textAllCaps="true"
-
TextView 使用 StaticLayout 或者 DynamicLayout 来自定义view替换它
-
重新 RecyclerView.onViewRecycled(holder) 回收资源
-
RecycleView.setItemViewCacheSize(size) 加大 RecyclerView 缓存空间,利用空间换时间策阅来提高流程性
-
如果多个 RecyclerView 的 adapter 是一样的,比如嵌套的 RecyclerView 存在一样的adapter,可以通过 RecyclerView.setRecycledViewPool(pool); 共有一个 RecycledViewPool
-
对于 ItemView 设置监听器,不要对每一个 item 都调用 addXxListener,应该共有一个 XxListener 根据 id 来进行不同操作,优化了对象的频繁创建带来的资源消耗。
-
通过 getExtraLayoutSpace 来增加 RecyclerView 预留的额外空间(显示范围内,应该额外缓存的空间) ,如下所示:
new LinearLayoutManager(this) {
@Override
protected int getExtraLayoutSpace(RecyclerView.State state) {
return size;
}
};
RecyclerView缓存机制
暂时没搞明白,欢迎一块儿探讨~
recyclerView算是Android中比较常用的控件了,平常的使用也很简单,没什么说的。 另外,recyclerView也经常会有嵌套使用和多条目布局的使用方法,嵌套使用时要注意处理是否有滑动冲突,多条目布局就是定义多个ViewHolder来区分不同的条目。
另外使用itemDecoration可以灵活控制布局之间的间距和装饰等效果。
缓存机制的话确实还没读过源码,找了篇技术文章,可以参考下:https://blog.csdn.net/wzy_1988/article/details/81569156
RecyclerView vs ListView ListView相比RecyclerView,有一些优点:
1.addHeaderView(), addFooterView()添加头视图和尾视图。 2.通过”android:divider”设置自定义分割线。 3.setOnItemClickListener()和setOnItemLongClickListener()设置点击事件和长按事件。
这些功能在RecyclerView中都没有直接的接口,要自己实现(虽然实现起来很简单),因此如果只是实现简单的显示功能,ListView无疑更简单。
RecyclerView相比ListView,有一些明显的优点:
1.默认已经实现了View的复用,不需要类似if(convertView == null)的实现,而且回收机制更加完善。 2.默认支持局部刷新。 3.容易实现添加item、删除item的动画效果。 4.容易实现拖拽、侧滑删除等功能。
RecyclerView是一个插件式的实现,对各个功能进行解耦,从而扩展性比较好。
找到一篇基本可以全面了解recyclerview的文章,可以参考下 https://www.jianshu.com/p/4f9591291365
RecyclerView vs ListView ListView相比RecyclerView,有一些优点:
1.addHeaderView(), addFooterView()添加头视图和尾视图。 2.通过”android:divider”设置自定义分割线。 3.setOnItemClickListener()和setOnItemLongClickListener()设置点击事件和长按事件。
这些功能在RecyclerView中都没有直接的接口,要自己实现(虽然实现起来很简单),因此如果只是实现简单的显示功能,ListView无疑更简单。
RecyclerView相比ListView,有一些明显的优点:
1.默认已经实现了View的复用,不需要类似if(convertView == null)的实现,而且回收机制更加完善。 2.默认支持局部刷新。 3.容易实现添加item、删除item的动画效果。 4.容易实现拖拽、侧滑删除等功能。
RecyclerView是一个插件式的实现,对各个功能进行解耦,从而扩展性比较好。
找到一篇基本可以全面了解recyclerview的文章,可以参考下 https://www.jianshu.com/p/4f9591291365
那你是如何解决下划线渲染两次的呢?
RecyclerView是Android 5.0推出的一个可以展示大量数据的、灵活的控件。
优势
- 支持线性,网格和瀑布流三种布局
- 强制实现ViewHolder
- 提供友好的ItemAnimator动画
- 解耦的架构设计
- 比ListView有更好的性能
重要组件
- LayoutManager:定位和布局Item
- ItemAnimator:Item动画
- Adapter:创建View并绑定数据
性能优化
- 使用LinearLayoutManager.setInitialPrefetchItemCount(count)设置嵌套在内部的横向RecyclerView的初次显示Item个数
- Item高度固定时(数据变化也不会导致Item高度变化),使用RecyclerView.setHasFixedSize(true)
- 多个RecyclerView共用一个RecyclerViewPool
- DiffUtil局部刷新
RecyclerView缓存
- 第一层Scrap & 第二层Cache:缓存当前屏幕的ItemView。都是根据position去缓存和获取复用ItemView,复用时不需要再调用onBindViewHolder()重新进行数据绑定了。
- 第三层ViewCacheExtension:开发者自定义的缓存策略
- 第四层缓存池:存放已经滑出屏幕的View,需要复用时根据item type去查找是否有可复用的ItemView,如果有可复用的就拿出来再重新进行数据绑定,没有就调用onCreateViewHolder()再新建一个ItemView。
RecyclerView 核心要点
RecycleView 是什么
A flexible view for providing a limited window into a large data set 为有限的屏幕显示大量的数据的灵活的视图

ListView 的局限
- 只支持纵向列表的滑动
- 没有支持动画的 API
- 接口设计和系统设计不一致
- setOnItemClickListener()
- setOnItemLongClickListener()
- setSelection()
- 没有强制实现 ViewHolder
- 没有实现ViewHolder 的 getView(); 每次都实现findViewById() DFS 浪费时间,消耗性能a/需要实现 ViewHolder 并且 ItemView.setTag(ViewHolder)
- 性能不如 RecycleView
RecycleView 的优势
- 默认支持 Linear,Grid,Staggered Grid 三种布局
- 友好的 ItemAnimator 动画 API
- 强制实现 ViewHolder
- 解耦的架构设计
- RecycleView
- LayoutManager
- ItemAnimator
- Adapter
- RecycleView
- 相比 ListView 更好的性能
RecycleView Demo
略
ViewHolder 究竟是什么
- ViewHolder 和 item view 是什么关系?一对一?一对多
- 一一对应
- 不使用 ViewHolder 还会实现复用吗
- 是复用的,这是无关的,会复用 convertView,只是 findViewById 消耗性能
RecyclerView 缓存机制
ListView 缓存机制

两层缓存机制 Active View:指屏幕能看到的 ItemView Android 刷新屏幕的时候会将屏幕中的 View 清空掉,再加进来,这时回收的使用的就是Active View,这时不需要重新绑定 Scrap View:屏幕中看不到的 ItemView

RecycleView 缓存原理

RecycleView 缓存的是 ViewHolder ,使用了四层缓存机制
- Scrap:屏幕内部的 ItemView,通过数据集的 Position 找到的,可以直接复用不需要绑定
- Cache:移出屏幕的 ItemView 放入一个CacheView(默认个数为2),就是比屏幕多两个 ItemView, 方便来回翻少量的View,可以直接复用不需要绑定
- ViewCacheExtension:用户自定义的缓存机制
**ViewCacheExtension Example ** 广告夹杂在 RecycleView 内,并且不会发生变化,广告和内容分开请求
- 广告卡片
- 每页一共有四个广告
- 短期内不会发生变化
- Cache 只关心 position,不关心 view type
- RecycleViewPool 只关心 view type,每次都要重新绑定
- 解决方法:在 ViewCacheExtension 保存四个广告的 Card
- RecycleViewPool:被废弃的 ItemView ,内部的 data 都是 dirty 的。通过ViewType来重新Bind数据的
注意广告的 impression
- ListView 通过 getView() 统计?准确!用户看到item的时候一定会通过 getView() 「即使发生了复用」
- RecycleView 通过 onBindView() 统计?错误!发生复用的时候不通过 onBindView() Scrap,Cache 不回发生重新绑定!
- RecycleView 通过 onViewAttachedToWindow() 统计!这时每当 ItemView 出现在用户视野里的时候都会回调。

可能不知道的 RecyclerView 性能优化策略
- 在 onBindViewHolder() 设置点击监听? 在 onBindViewHolder() 里设置点击监听器会导致重复创建点击监听器,会造成内存抖动 改善: 在 onCreateViewHolder()/ViewHolder Constructor 里设置点击监听,ItemView - ViewHolder - View.OnClickListener 三者一一对应 也可以只设置一个 ViewOnClickListener ,坏处是处理逻辑都在一起,还会造成数据耦合严重(要取出点击View的对象)
- LinearLayoutManager.setInitialPrefetchItemCount()
- 内部嵌套中的 RecycleView ,用户滑动到横向滑动的item RecycleView 的时候,由于需要创建更复杂的 RecycleView 及多个子 View,可能会导致页面卡顿
- 由于 RenderThread(分担主线程的渲染压力,放到这里来执行) 的存在,RecycleView 会进行 prefetch (API 21⬆️)
- LinearLayoutManager.setInitialPrefetchItemCount(横向列表初始显示时可见的 item 个数)
- 只有 LinearLayoutManager 有这个 API
- 只有在嵌套在内部的RecycleView 才会生效

-
RecycleView.setHasFixedSize(true)
伪代码:
void onContentsChanged(){ if(mHasFixedSize){ layoutChildren(); } else { requestLayout(); } ]如果更改数据不会造成每个 ItemView 的不会影响RecyclerView的宽高的时候可以设置这个代码,避免Item的重新绘制
-
多个 RecyclerView 共用一个 RecycleViewPool 默认是每一个 RecyclerView 用自己的 RecycleViewPool,可以通过 API 设置共享缓存池
RecyclerView.RecycledViewPool recycledViewPool = new RecyclerView.RecycledViewPool(); mRecyclerView.setRecycledViewPool(recycledViewPool); mRecyclerView1.setRecycledViewPool(recycledViewPool); mRecyclerView2.setRecycledViewPool(recycledViewPool); -
getViewType
-
DiffUtil
- 局部更新方法 notifyItemXXX() 不适用于所有情况
- notifyDataSetChange() 会导致整个布局重回,重新绑定所有 ViewHolder,而且会可能失去可能的动画效果
- DiffUtil 适用于整个页面需要刷新,但是有部分数据可能相同的情况
- 内部算法使用的动画规划算法
private DiffUtil.Callback mDiffUtilCallBack = new DiffUtil.Callback() { //旧列表的长度 @Override public int getOldListSize() {return 0;} //新列表的长度 @Override public int getNewListSize() {return 0;} //列表项的ID是否变化了 @Override public boolean areItemsTheSame(int i, int i1) {return false;} //列表项的内容是否变化了 @Override public boolean areContentsTheSame(int i, int i1) {return false;} //列表项的哪些内容变化了 @Nullable @Override public Object getChangePayload(int oldItemPosition, int newItemPosition) { return super.getChangePayload(oldItemPosition, newItemPosition); } };
- 在数据量很大的时候异步计算diff
- 使用 Thread/Handler 将 DiffResult 发送到主线程
- 使用RxJava 将 calculateDiff 操作放到后台线程执行
- 使用 Google 提供 AsyncListDiff(Executor)/ListAdapter
为什么 ItemDecoration 可以绘制分割线

还能做什么?
- 划分割线
- 高亮
- 从视觉上对视图分组 Section
RecyclerView内部缓存机制是四级缓存:Scrap、Cache、ViewCacheExtension、RecycledViewPool,RecyclerView是通过LayoutManager里面的Recycler来管理缓存; 1、Scrap:屏幕内部的ItemView,通过数据集的position来找到对应的Item,可以直接取过来用; 2、Cache:刚移出屏幕的ItemView,放到Cache里,当Cache里的ItemView重新进入屏幕时,也是通 过position来找到对应的Item,直接可以使用,不需要走bindViewHolder()。Cache和 Scrap 一样,都是可以直接通过position来找到对应的Item,不需要重新绑定; 3、ViewCacheExtension:自定义缓存,如果有自定义,需要在这里面找,没有的话直接跳过; 4、RecycledViewPool:所有被废弃的ItemView的Pool,该pool里面的Item都是dirty的,需要通过 ViewType来找到数据,找到数据的话,需要重新绑定,不走createViewHodler(),走bindViewHolder()。
据我观察,公用一个 RecycledViewPool,不能达到完美的优化效果,只是节省了内存. ` RecyclerView.RecycledViewPool recycledViewPool = new RecyclerView.RecycledViewPool();
mRecycledViewPool.setMaxRecycledViews(1, 10); mRecycledViewPool.setMaxRecycledViews(2, 10); mRecycledViewPool.setMaxRecycledViews(3, 10); mRecycledViewPool.setMaxRecycledViews(4, 10); mRecycledViewPool.setMaxRecycledViews(5, 10);
mRecyclerView1.setRecycledViewPool(recycledViewPool); mRecyclerView2.setRecycledViewPool(recycledViewPool); mRecyclerView3.setRecycledViewPool(recycledViewPool); `
比如 mRecyclerView1 type=1,保存了 5个. 在 mRecyclerView2 传入 recycledViewPool,并没有 mRecyclerView1 type=1的5个缓存. 所以,当 mRecyclerView2 第一次 type=1 出来的时候,还是会执行 onCreateViewHolder,onBindViewHolder... ... ????