1.局部刷新

1)避免整个列表的数据更新,只更新受影响的布局。例如,加载更多时,不使用notifyDataSetChanged(),而是使用notifyItemRangeInserted(rangeStart, rangeEnd)

补充:recyclerView.adapter的刷新:

  • 刷新全部可见的item,notifyDataSetChanged()
  • 刷新指定item,notifyItemChanged(int)   position数据发生了改变,那调用这个方法,就会回调对应position的onBindViewHolder()方法了,当然,因为ViewHolder是复用的,所以如果position在当前屏幕以外,也就不会回调了,因为没有意义,下次position滚动会当前屏幕以内的时候同样会调用onBindViewHolder()方法刷新数据了。其他的方法也是同样的道理。
  • 从指定位置开始刷新指定个item,notifyItemRangeChanged(int,int)   顾名思义,可以刷新从positionStart开始itemCount数量的item了(这里的刷新指回调onBindViewHolder()方法)。
  • 插入、移动一个并自动刷新,notifyItemInserted(int)、notifyItemMoved(int)、notifyItemRemoved(int)

(2)配合DiffUtil局部刷新

官方提供的上面这些局部刷新方法不太好用,因为场景不太好控制;,就轮到了DiffUtil登场

DiffUtil是 Support-v7:24:2.0 中,中更新的工具类,主要是为了配合RecyclerView使用,通过比对新、旧两个数据集的差异,生成旧数据到新数据的最小变动,然后对有变动的数据项,进行局部刷新。

DiffUtil.Callback  :具体用于限定数据集比对规则 ,内部主要有如下5个比较方法:

public abstract static class Callback {

        public abstract int getOldListSize();//旧数据集长度

        public abstract int getNewListSize();//新数据集长度

        //判断新旧数据是否无变化
public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition); /**
* Called by the DiffUtil when it wants to check whether two items have the same data.
* DiffUtil uses this information to detect if the contents of an item has changed.
* <p>
* DiffUtil uses this method to check equality instead of {@link Object#equals(Object)}
* so that you can change its behavior depending on your UI.
* For example, if you are using DiffUtil with a
* {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, you should
* return whether the items' visual representations are the same.
* <p>
* This method is called only if {@link #areItemsTheSame(int, int)} returns
* {@code true} for these items.
*
* @param oldItemPosition The position of the item in the old list
* @param newItemPosition The position of the item in the new list which replaces the
* oldItem
* @return True if the contents of the items are the same or false if they are different.
*/
public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition);//如果item相同,此方法用于判断是否同一个 Item 的内容也相同; /**
* When {@link #areItemsTheSame(int, int)} returns {@code true} for two items and
* {@link #areContentsTheSame(int, int)} returns false for them, DiffUtil
* calls this method to get a payload about the change.
* <p>
* For example, if you are using DiffUtil with {@link RecyclerView}, you can return the
* particular field that changed in the item and your
* {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} can use that
* information to run the correct animation.
* <p>
* Default implementation returns {@code null}.
*
* @param oldItemPosition The position of the item in the old list
* @param newItemPosition The position of the item in the new list
*
* @return A payload object that represents the change between the two items.
*/
@Nullable
public Object getChangePayload(int oldItemPosition, int newItemPosition) {//如果item相同,内容不同,用 payLoad 记录这个 ViewHolder 中,具体需要更新那个View
return null;//getChangePayload() 默认返回 null ,即整个item 全部刷新。
}
}

 

一般在getChangePayload()方法中调用super.getChangePayload() 即可,不做精细化刷新。

如果一个item 非常复杂,存在里面某个View 数据刷新,可以利用payLoad参数来实现,对应修改点 在 onBindViewHolder(HelloViewHolder holder, int position ) 的基础上实现带参的 onBindViewHolder ,同时在getChangedPayLoad()对应实现。

从上面分析可知:areItemsTheSame()、areContentsTheSame()、getChangePayload() 分别代表了不同量级的刷新。

DiffUtil.DiffResult

DiffUtil.DiffResult : 比对数据集之后,返回的差异结果 ,通过DiffUtil.calculateDiff(diffCallback)(当数据量较大时,建议放在子线程中调用)得到。

DiffUtil.DiffResult::dispatchUpdatesTo() 根据diff 数据结果,选择刷新方式。

public void dispatchUpdatesTo(final RecyclerView.Adapter adapter) {
dispatchUpdatesTo(new ListUpdateCallback() {
@Override
public void onInserted(int position, int count) {
adapter.notifyItemRangeInserted(position, count);
} @Override
public void onRemoved(int position, int count) {
adapter.notifyItemRangeRemoved(position, count);
} @Override
public void onMoved(int fromPosition, int toPosition) {
adapter.notifyItemMoved(fromPosition, toPosition);
} @Override
public void onChanged(int position, int count, Object payload) {
adapter.notifyItemRangeChanged(position, count, payload);
}
});
}
 总结:使用起来比较简单 ,1)实现DiffUtil.Callback   接口 ;2)新老数据集通过DiffUtil.calculateDiff 计算得到DiffUtil.DiffResult  ;3)DiffUtil.DiffResult::dispatchUpdatesTo()  刷新数据。
 
参考:https://www.jianshu.com/p/8514d5c0ee60

2.使用一个监听事件

  • onclickListener只new一个,通过position确定具体事件

3.如果可能所有元素相同的高度 ,避免重复计算大小,提高绘制速度;

我们看一下原理:

使用:

recyclerView.setHasFixedSize(true);

源码:

 /**
* RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
* size is not affected by the adapter contents. RecyclerView can still change its size based
* on other factors (e.g. its parent's size) but this size calculation cannot depend on the
* size of its children or contents of its adapter (except the number of items in the adapter).
* <p>
* If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
* RecyclerView to avoid invalidating the whole layout when its adapter contents change.
*
* @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
*/
public void setHasFixedSize(boolean hasFixedSize) {
mHasFixedSize = hasFixedSize;
}

官方说明大概意思:当提前知道adapter的内容改变不会影响尺寸时,可以设置为true避免重复计算来进行优化。

注意:当setHasFixedSize为true时,再调用notifyDataSetChanged(),发现大小还是重新计算了,看来理解出现错误了。

接着看mHasFixedSize属性:

在triggerUpdateProcessor中看到

 void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}

可以看到triggerUpdateProcess方法被

onItemRangeChanged(),

onItemRangeInserted(),

onItemRangeRemoved(),

onItemRangeMoved()

这几个方法调用

这样看就很明白了,当调用Adapter的增删改插方法,最后就会根据mHasFixedSize这个值来判断需要不需要requestLayout();

再来看一下notifyDataSetChanged()执行的代码,最后是调用了onChanged,调用了requestLayout(),会去重新测量宽高。

再来看一下notifyDataSetChanged()执行的代码,最后是调用了onChanged,调用了requestLayout(),会去重新测量宽高。

 @Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
mState.mStructureChanged = true; setDataSetChangedAfterLayout();
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}

当我们确定Item的改变不会影响RecyclerView的宽高的时候可以设置setHasFixedSize(true),并通过Adapter的增删改插方法去刷新RecyclerView,而不是通过notifyDataSetChanged()。(其实可以直接设置为true,当需要改变宽高的时候就用notifyDataSetChanged()去整体刷新一下)
参考:https://www.jianshu.com/p/79c9c70f6502

4.减少item布局层级

  • 使用 ConstraintLayout
  • 选择合适的布局容器,使用merge标签

5.缓存池recyclerViewPool

在viewPager+recyclerView场景下使用recyclerViewPool复用

 /**
* RecycledViewPool lets you share Views between multiple RecyclerViews.
* <p>
* If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
* and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
* <p>
* RecyclerView automatically creates a pool for itself if you don't provide one.
*
*/
public static class RecycledViewPool {。。。}

首先看官方说明:大概意思是你可以在多个recyclerView中设置对象池来共享viewHolder对象,降低创建ViewHolder开销;即使你不手动创建也会自动创建一个。

RecycledViewPool的使用

RecycledViewPool使用起来也是非常的简单:先从某个RecyclerView对象中获得它创建的RecycledViewPool对象,或者是自己实现一个RecycledViewPool对象,然后设置个接下来创建的每一个RecyclerView即可。

需要注意的是,如果你使用的LayoutManager是LinearLayoutManager或其子类(如GridLayoutManager),需要手动开启这个特性:

layout.setRecycleChildrenOnDetach(true)
RecyclerView view1 = new RecyclerView(context);
LinearLayoutManager layout = new LinearLayoutManager(context);
layout.setRecycleChildrenOnDetach(true);
view1.setLayoutManager(layout);
RecycledViewPool pool = view1.getRecycledViewPool();
//...
RecyclerView view2 = new RecyclerView(context);
//... (set layout manager)
view2.setRecycledViewPool(pool);
//...
RecyclerView view3 = new RecyclerView(context);
//...(set layout manager)
view3.setRecycledViewPool(pool);

viewPAger中使用示例:

public class PagerActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pager);
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(new PageAdapter(getSupportFragmentManager()));
}
static class PageAdapter extends FragmentPagerAdapter{
RecyclerView.RecycledViewPool mPool = new RecyclerView.RecycledViewPool();
public PageAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int i) {
RecyclerViewFragment f = new RecyclerViewFragment();
f.mPool = mPool;
return f;
}
// ...
}
public static class RecyclerViewFragment extends Fragment{
RecyclerView.RecycledViewPool mPool;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
RecyclerView view = new RecyclerView(inflater.getContext());
LinearLayoutManager layout = new LinearLayoutManager(inflater.getContext());
layout.setRecycleChildrenOnDetach(true);
view.setLayoutManager(layout);
if (mPool != null) {
view.setRecycledViewPool(mPool);
}
view.setAdapter(...);
return view;
}
// ...
}
}

ps:

RecycledViewPool是依据ItemViewType来索引ViewHolder的,所以你必须确保共享的RecyclerView的Adapter是同一个,或view type 是不会冲突的。

RecycledViewPool可以自主控制需要缓存的ViewHolder数量: 
mPool.setMaxRecycledViews(itemViewType, number); 毕竟池子里的水并不是越深越好。

RecyclerView可以设置自己所需要的ViewHolder数量,只有超过这个数量的detached ViewHolder才会丢进ViewPool中与别的RecyclerView共享。

recyclerView.setItemViewCacheSize(10);

在合适的时机,RecycledViewPool会自我清除掉所持有的ViewHolder对象引用,不用担心池子会“侧漏”。当然你也可以在你认为合适的时机手动调用clear()

参考:https://blog.csdn.net/axi295309066/article/details/52741810

问题:你的项目中哪种优化,效果最明显

 

【RecyclerView优化】的更多相关文章

  1. Android 性能优化总结

    App 流畅运行,是用户体验的第一步.App 流程运行,涉及到性能优化,主要涉及到布局优化, 绘制优化,内存泄漏优化,响应速度优化,列表展示优化,Bitmap 优化,线程优化,包大小优化. 布局优化 ...

  2. RecyclerView.Adapter优化了吗?

    昨天写了一篇「还在用ListView?」讲的内容是RecyclerView的使用技巧以及一些经常使用的开源库.有朋友反馈"我已经在用recyclerview了",那么怎样让它更好用 ...

  3. Android瀑布流优化,解决Recyclerview展示大批量图片时Item自动切换、闪烁、空白等问题

    本文涉及的代码案例可以在下方的链接中找到,如果对你有帮助,请给个Star(#^.^#) https://github.com/CodeTillDoom/StaggeredRcl 问题分析 这段时间业务 ...

  4. recyclerView 列表类控件卡顿优化

    1.使用ConstraintLayout减少布局层级. 2.可以的话,设置RecyclerView布局等高,然后设置recyclerView.setHasFixedSize(true)这样可以避免每次 ...

  5. Android RecyclerView 滑动时图片加载的优化

    RecyclerView 滑动时的优化处理 在滑动时停止加载图片,在滑动停止时开始加载图片,这里用了Glide.pause 和Glide.resume.这里为了避免重复设置增加开销,设置了一个标志变量 ...

  6. Android RecyclerView使用 及 滑动时加载图片优化方案

    1.控制线程数量 + 数据分页加载2.重写onScrollStateChanged方法 这个我们后面再谈,下面先来看看RecyclerView控件的使用及我们为什么选择使用它 RecyclerView ...

  7. Android 滚动RecyclerView加载图片时的流畅度优化

    实现:使用onScrollStateChanged回调检测滚动状态,并在RecyclerViewAdapter内部设置类似isScrolling的状态值来控制网络图片的加载. 下面是代码举例: // ...

  8. 带你实现开发者头条APP(五)--RecyclerView下拉刷新上拉加载

    title: 带你实现开发者头条APP(五)--RecyclerView下拉刷新上拉加载 tags: -RecyclerView,下拉刷新,上拉加载更多 grammar_cjkRuby: true - ...

  9. 带你实现开发者头条APP(四)---首页优化(加入design包)

    title: 带你实现开发者头条APP(四)---首页优化(加入design包) tags: design,Toolbar,TabLayout,RecyclerView grammar_cjkRuby ...

随机推荐

  1. remove duplicate of the sorted array

    description: Given a sorted array, remove the duplicates in place such that each element appear only ...

  2. Mysql 查询条件中字符串尾部有空格也能匹配上的问题

    一.表结构 TABLE person id name 1 你 2 你(一个空格) 3 你(二个空格) 二.查询与结果 select * from person where `name` = ? 无论 ...

  3. window.open打开新窗体并用post方式传参

    function openPostWindow(url,data,name){ //url要跳转到的页面,data要传递的数据,name显示方式(可能任意命名) var tempForm = docu ...

  4. 简明4步,让Python的好朋友Pycharm变得更加雅观!

    跟着人工智能/机械学习的兴起,Python再次取得广泛程序员的关注.而JetBrains出品的PyCharm无疑是最好用的Python IDE之一. 把本身的IDE装备安排得既有逼格又雅观,这是每个P ...

  5. PAT1101:Quick Sort

    1101. Quick Sort (25) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CAO, Peng There is a ...

  6. jQuery-01:on live bind delegate

    摘自:https://www.cnblogs.com/moonreplace/archive/2012/10/09/2717136.html moonreplace这位大牛的 当我们试图绑定一些事件到 ...

  7. Linux时间子系统之(十五):clocksource

    专题文档汇总目录 Notes:clocksource基本概念,struct clocksource详解:注册和注销clocksource:内核如何选取clocksource:clocksource相关 ...

  8. 可能是迄今为止最好的GitHub代码浏览插件--赞

    https://chrome.google.com/webstore/category/extensions?hl=zh-CN 搜索"insight.io" chrome://ex ...

  9. XML解析的四种方法 建议使用demo4j解析 测试可以用

    https://www.cnblogs.com/longqingyang/p/5577937.html 4.DOM4J解析  特征: 1.JDOM的一种智能分支,它合并了许多超出基本XML文档表示的功 ...

  10. jmeter添加断言

    先创建一个线程组,再创建一个http请求. 为了方便观察,我们添加两个监听器,察看结果树和断言结果. 添加断言:响应断言,响应断言也是比较常用的一个断言 设置响应断言:正常情况下响应代码是200.选择 ...