只能复用于指定位置
最大储存
mViewCacheMax = mRequestedCacheMax + extraCache
(
extraCache
是由
prefetch
的时候计算出来的)
用于解决
RecyclerView
滑动抖动时的情况,还有用于保存
Prefetch
mViewCacheExtension : 开发者可自定义的一层缓存,是虚拟类ViewCacheExtension的一个实例,开发者可实现方法
getViewForPositionAndType(Recycler recycler, int position, int type)
来实现自己的缓存
适用场景(自备梯子)
Pool : 回收池
1.onBindViewHolder
1.不要在
onBind
设置监听。因为
mRecyclerPool
取
ViewHolder
时要重新调用
onBind
。
onClickListener
应设置在
ViewHolder
。
/*=========== Adapter ===========*/
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
// 把item对应的实体类传给itemView,方便点击事件发生时得到对应的资源。
viewHolder.itemView.setTag(i);
class ViewHolder extends RecyclerView.ViewHolder {
private LinearLayout mItem;
ViewHolder(@NonNull final View itemView) {
mItem = itemView.findViewById(R.id.ll_item);
// onClickListener 不要在 onBind 的时候设置,因为 mRecyclerPool 取 ViewHolder 时要重新调用 onBind
mItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != mItemClick) {
// 设置点击监听
mItemClick.onItemClick(v, (int) itemView.getTag());
// Item监听
private OnRvItemClick mItemClick;
// 设置监听
public void setItemClick(OnRvItemClick mItemClick) {
this.mItemClick = mItemClick;
public interface OnRvItemClick {
// 点击项事件
void onItemClick(View v, int position);
/*=========== Other ===========*/
// 设置点击项监听事件
Adapter.setItemClick(new OnRvItemClick() {
@Override
public void onItemClick(View v, int data) {
mAdapter.getItem(data);
2.不要在onBindViewHolder
做逻辑判断和计算
bindViewHolder
方法是在UI线程进行的,如果在该方法进行耗时操作,将会影响滑动的流畅性。
2.设置高度固定
如果RecyclerView
固定宽高,只是用于展示固定大小的组件,可设置recyclerView.setHasFixedSize(true)
这样可避免每次绘制Item
时,不再重新计算高度。
RecyclerView.setHasFixedSize(true);
3.重写 onScroll
对于大量图片、复杂布局的RecyclerView
考虑重写onScroll
事件,滑动暂停后再加载。
// 空闲状态
RecyclerView.SCROLL_STATE_IDLE
// 滚动状态
RecyclerView.SCROLL_STATE_FLING
// 触摸后状态
RecyclerView.SCROLL_STATE_TOUCH_SCROLL
// 添加滑动监听
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
// 空闲状态时加载图片(数据)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
Glide.with(mContext).resumeRequests();
}else {
Glide.with(mContext).pauseRequests();
4.最大程度不使用 notifyDataSetChanged
对于新增或删除数据通过DiffUtil
,来进行局部数据刷新,而不是一味的全局刷新数据。
DiffUtil是support包下新增的一个工具类,用来判断新数据和旧数据的差别,从而进行局部刷新。
DiffUtil的使用,在原来调用mAdapter.notifyDataSetChanged()的地方:
// 设置数据
public void setData(final List<String> newData) {
//notifyDataSetChanged();
// 数据优化
// 对于新增或删除数据通过DiffUtil,来进行局部数据刷新,而不是一味的全局刷新数据。
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
@Override
public int getOldListSize() {
return getItemCount();
@Override
public int getNewListSize() {
return newData == null ? 0 : newData.size();
@Override
public boolean areItemsTheSame(int i, int i1) {
return true;
@Override
public boolean areContentsTheSame(int i, int i1) {
return false;
diffResult.dispatchUpdatesTo(this);
this.mData = newData;
5.减少每个 ItemView 的层级嵌套,减少过度绘制
6.刷新闪烁
设置稳定Id
eg:当调用notifyDataSetChanged
的时候,recyclerView
不知道到底发生了什么,所以它只能认为所有的东西都发生了变化,即,将所有的viewHolder
都放入到pool
中。
但是,如果我们设置了stable ids
,那么就会不一样了:viewHolder
被放入了scrap
中,而不是pool
中。注意,这里,它的性能提升了很多!
不用重新绑定,重新创建新的viewHolder
,不用重新addView
。addView
会导致重新测量…
原本我们需要调用notifyItemMoved(4, 6)
,但是现在直接调用notifyDataSetChanged()
就好了,但是测试结果不会有动画效果。
Adapter.setHasStableIds(true);
7.使用 RecyclerView Prefetch 功能
Prefetch 功能
横向嵌套,利用RecyclerView
数据预取功能。
// 在嵌套内部的LayoutManager中调用LinearLayoutManger的设置方法
// num的取值:如果列表刚刚展示4个半item,则设置为5
innerLLM.setInitialItemsPrefetchCount(num);
Ⅱ、缓存优化
1.mCachedViews
设置mCachedViews
大小
eg:我们有一个壁纸库的列表,用户经常会上下(左右)滑动,那么我们增加cache
的容量,就会获得更好得性能。
然而对于feed流之类的列表,用户很少返回,所以增加cache
容量意义不大。
RecyclerView.setItemViewCacheSize();
2.RecycledViewPool
设置每个类型储存的容量
eg1:在我们滑动的过程中,一个类型的viewHolder
在pool
中应该一直只会存在一个(除非你使用了GridLayoutManager
),所以,如果你的pool
中存在多个viewHolder
的话,他们在滚动过程中基本上是无用的。
eg2:当我们调用notifyDataSetChanged()
或者notifyItemRangeChanged(i, c)
(c
这个范围非常大的时候),那么很多viewHolder
都会最终被放入到pool
中,因为pool
只能放置5个,那么多余的就会被丢弃,等待回收。最重要的是会重新create
与bind
对性能影响比较大。如果你的列表能够容纳很多行,而且使用notifyDataSetChanged
方法比较频繁,那么你应该考虑设置一下容量大小。
RecyclerView.getRecycledViewPool().setMaxRecycledViews(0, 20);
多个列表公用一个RecycledViewPool
eg1:RecyclerView
嵌套RecyclerView
考虑设置RecyclerPool
缓存
RecyclerView.setRecycledViewPool();
// RecyclerView + RecyclerView,每个item内部RecyclerView设同一个pool
private RecyclerView.RecycledViewPool childPool;
public XXAdapter(){
childPool = new RecyclerView.RecycledViewPool();
private class RcyViewHolder extends RecyclerView.ViewHolder {
private SRecyclerView sRcy;
public RcyViewHolder(View itemView) {
super(itemView);
sRcy = itemView.findViewById(R.id.rcy_child);
LinearLayoutManager manager = new LinearLayoutManager(mContext);
// 1.设置回收
manager.setRecycleChildrenOnDetach(true);
manager.setOrientation(LinearLayoutManager.HORIZONTAL);
sRcy.setLayoutManager(manager);
// 2.设置缓存Pool
sRcy.setRecycledViewPool(childPool);
Ⅲ、其他优化
分页加载远端数据,对拉取的远端数据进行缓存,提高二次加载速度。
四、常见解决
1.局部刷新
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads) {
if (payloads.isEmpty()) {
onBindViewHolder(holder, position);
} else {
// 局部刷新
holder.setTitle("局部更新");
// 局部刷新测试
mAdapter.notifyItemChanged(2, "payload");