解决scrollView嵌套RecyclerView导致RecyclerView复用ViewHolder失效

现状

  • 在嵌套使用时,如果RecyclerView的adapter有1000条数据,则RecyclerView会创建1000个ViewHolder,每个ViewHolder会持有一个itemView。ViewHolder和itemView的快速大量创建容易anr

问题分析

  • 最终发现LinearLayoutManager中的fill方法会导致adapter中的onCreateViewHolder方法调用,
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
       ....
       // 这个地方为true会导致adapter的onCreateViewHolder调用,正常情况下layoutState.mInfinite为false,影响recycleView子childView数量的是
        // remainingSpace > 0和layoutState.hasMore(state)
       while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
          .....
       }       
   }
  • 如上代码所示 while循环的条件有三个,正常的使用recycleView(不用scrollview嵌套reycleView),layoutState.mInfinite为false,嵌套的时候layoutState.mInfinite为true,我们看看ayoutState.mInfinite的解释


    image2020-9-22_10-5-9.png
  • 从注释来看,mInfinite在它为true时,LinearLayoutManager是不会限制recycleView的childView的个数,从而导致recycleview复用失效
  • 再看看mInfinite的赋值
    image2020-9-22_10-7-50.png
  • 继续跟踪看看OrientationHelper的实现
  public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) {
        return new OrientationHelper(layoutManager) {
            .......
            @Override
            public int getEnd() {
                return mLayoutManager.getHeight();
            }

            @Override
            public int getMode() {
                return mLayoutManager.getHeightMode();
            }
            ......
           
        };
    }
  • 可以看到OrientationHelper的getEnd()和getMode()最后都是受mLayoutManager的HeightSepc(里面是height的size和heightMode)影响

解决方案

  • 自定义view,并继承RecyclerView,重写onMeasure方法,对heightSpec进行判断,如果是是 MeasureSpec.UNSPECIFIED模式,改为MeasureSpec.AT_MOST
public class CustomRecyclerView extends RecyclerView {


    public CustomRecyclerView(@NonNull Context context) {
        super(context);
    }

    public CustomRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        //MeasureSpec.UNSPECIFIED 模式会导致LinearLayoutManager中的fill方法中判断是否可以无限制填冲整个recycleview为true,从而导致RecyclerView的复用失效
        if (MeasureSpec.getMode(heightSpec) == MeasureSpec.UNSPECIFIED) {
            int size = MeasureSpec.getSize(heightSpec);
            heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
        }
        super.onMeasure(widthSpec, heightSpec);
    }
}

你可能感兴趣的:(解决scrollView嵌套RecyclerView导致RecyclerView复用ViewHolder失效)