相关文章推荐
急躁的绿茶  ·  opencv keras python - ...·  6 月前    · 
高大的茶壶  ·  Thread.Sleep 方法 ...·  1 年前    · 
有腹肌的熊猫  ·  par() ggplot-掘金·  1 年前    · 
  • 只能复用于指定位置
  • 最大储存 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,不用重新addViewaddView会导致重新测量…
  • 原本我们需要调用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:在我们滑动的过程中,一个类型的viewHolderpool中应该一直只会存在一个(除非你使用了GridLayoutManager),所以,如果你的pool中存在多个viewHolder的话,他们在滚动过程中基本上是无用的。
    eg2:当我们调用notifyDataSetChanged()或者notifyItemRangeChanged(i, c)c这个范围非常大的时候),那么很多viewHolder都会最终被放入到pool中,因为pool只能放置5个,那么多余的就会被丢弃,等待回收。最重要的是会重新createbind对性能影响比较大。如果你的列表能够容纳很多行,而且使用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");