【RecyclerView优化】

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个比较方法:
publicabstractstaticclass Callback {publicabstractint getOldListSize();//旧数据集长度
publicabstractint getNewListSize();//新数据集长度
        //判断新旧数据是否无变化
publicabstractboolean 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.
*/
publicabstractboolean 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
returnnull;//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 数据结果,选择刷新方式。
publicvoid dispatchUpdatesTo(final RecyclerView.Adapter adapter) {            dispatchUpdatesTo(
new ListUpdateCallback() {                @Override
publicvoid onInserted(int position, int count) {                    adapter.notifyItemRangeInserted(position, count);
                }
                @Override
publicvoid onRemoved(int position, int count) {                    adapter.notifyItemRangeRemoved(position, count);
                }
                @Override
publicvoid onMoved(int fromPosition, int toPosition) {                    adapter.notifyItemMoved(fromPosition, toPosition);
                }
                @Override
publicvoid 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.*/publicvoid 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(),会去重新测量宽高。
 @Overridepublicvoid 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.
     *
*/publicstaticclass 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中使用示例:
publicclass PagerActivity extends AppCompatActivity {    @Override
protectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);        setContentView(R.layout.activity_pager);
        ViewPager pager 
= (ViewPager) findViewById(R.id.pager);        pager.setAdapter(
new PageAdapter(getSupportFragmentManager()));    }
staticclass 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;        }
// ...    }
publicstaticclass 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
问题:你的项目中哪种优化,效果最明显
 原文出处:https://www.cnblogs.com/ivoo/p/10722677.html
以上是 【RecyclerView优化】 的全部内容, 来源链接: utcz.com/z/508846.html

