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. js实用技巧

    IIFE(立即调用函数表达式)最广泛的用途是避免污染全局作用域. 已经有很多JavaScript库和JavaScript高手正在使用这种技巧, 尤其是在最流行jQuery插件的开发者中. 你也应该把这 ...

  2. web 文件下载(.shp)

    1. Open IIS Manager2. Select MIME Types 3. In the right pane, click Add…4. Enter the following infor ...

  3. Effective C++ 读书笔记(13-32)

    条款一十三:以对象管理资源 1.把资源放进对象内,我们便可依赖C++的“析构函数自动调用机制“确保资源被释放. 2.auto_ptr是个”类指针对象“,也就是所谓”智能指针“,其析构函数自动对其所指对 ...

  4. 微信对账单处理-PHP

    最近要做支付对账,即检查第三方支付与数据库中账单是否一一对应,涉及到微信对账单的处理,成功时,微信账单接口返回数据以文本表格的方式返回,第一行为表头,后面各行为对应的字段内容,字段内容跟查询订单或退款 ...

  5. Linux用户登录日志查询

    # 1 utmp.wtmp.btmp文件 Linux用户登录信息放在三个文件中: 1 /var/run/utmp:记录当前正在登录系统的用户信息,默认由who和w记录当前登录用户的信息,uptime记 ...

  6. Spring Security 源码分析(四):Spring Social实现微信社交登录

    社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ.人人网.开心网.新浪微博.搜狐微博.腾讯微博.淘宝.豆瓣.MSN.Google等社会化媒体账号登录该网站. 前言 ...

  7. JavaWeb(四)Servlet-1

    1.Servlet 简介 Java Servlet是和平台无关的服务器端组件,它运行在Servlet容器中.Servlet容器负责Servlet和客户的通信以及调用Servlet的方法,Servlet ...

  8. SQL查询中用到的函数

    数据库表  students      id name sex age address 101 张汉 男 14 杭州 102 欧阳钦 男 13 杭州 103 吴昊 男 14 北京 104 钱进进 男 ...

  9. Python爬虫进阶六之多进程的用法

    前言 在上一节中介绍了thread多线程库.python中的多线程其实并不是真正的多线程,并不能做到充分利用多核CPU资源. 如果想要充分利用,在python中大部分情况需要使用多进程,那么这个包就叫 ...

  10. killall 、kill 、pkill 命令详解

    killall 命令 Linux系统中的killall命令用于杀死指定名字的进程(kill processes by name).我们可以使用kill命令杀死指定进程PID的进程,如果要找到我们需要杀 ...