RecyclerView 绘制、复用、对比ListView

本文深入探讨了RecyclerView的绘制流程,包括onMeasure、onLayout和onDraw,并详细阐述了其缓存复用机制,对比了ListView的缓存策略。RecyclerView通过LayoutManager、Adapter和Recycler实现高效的视图复用,涉及四个级别的缓存,增强了列表性能。
摘要由CSDN通过智能技术生成

之前学习ListView是为了更好地学习RecyclerView,没想到拖了这么久。

RecyclerView比ListView更加地复杂,主要从两个方面学习:

  1. RecyclerView 绘制流程
  2. RecyclerView 缓存复用
  3. ListView和RecyclerView缓存比较

RecyclerView 涉及的类

  1. LayoutManager : 负责子View的measure及layout
  2. Adapter: 适配器、负责创建子View和绑定数据到子View中
  3. Recycler:ViewHolder的回收和复用,有四种类型的缓存:crapped、cached、exCached、recycled

[1.0] RecyclerView 绘制流程

[1.1] onMeasure 测量

protected void onMeasure(int widthSpec, int heightSpec) {
   
    if (mLayout == null) {
    // LayoutManager不存在的情况
        // 默认测量,见小节1.1.1
        defaultOnMeasure(widthSpec, heightSpec);
        return;
    }
    // 需要自动测量
    if (mLayout.mAutoMeasure) {
   
        final int widthMode = MeasureSpec.getMode(widthSpec);
        final int heightMode = MeasureSpec.getMode(heightSpec);
        // 如果是宽高都是固定数值,skipMeasure为true,跳过测量步骤
        final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
                && heightMode == MeasureSpec.EXACTLY;
        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
        // 跳过测量步骤
        if (skipMeasure || mAdapter == null) {
   
            return;
        }
        if (mState.mLayoutStep == State.STEP_START) {
    
            // 收集ItemView的ViewHolder的信息并保存,见小节1.1.2
            dispatchLayoutStep1();
        }
        mLayout.setMeasureSpecs(widthSpec, heightSpec);
        // 实际测量
        dispatchLayoutStep2();
        mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
        // 如果recyclerview 有额外的宽高,需要二次测量  
        if (mLayout.shouldMeasureTwice()) {
   
            // 真正测量,见小节1.1.3
            dispatchLayoutStep2();
            mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
        }
    } else {
   
       //若自定义LayoutManager,自行实现
    }
}
[1.1.1] defaultOnMeasure

当不存在LayoutManager时候,会继续默认测量

void defaultOnMeasure(int widthSpec, int heightSpec) {
   
    // 根据measureSpec算出一个具体数值
    final int width = LayoutManager.chooseSize(widthSpec,
            getPaddingLeft() + getPaddingRight(),
            ViewCompat.getMinimumWidth(this));
    final int height = LayoutManager.chooseSize(heightSpec,
            getPaddingTop() + getPaddingBottom(),
            ViewCompat.getMinimumHeight(this));

    // 设置测量大小
    setMeasuredDimension(width, height);
}

// LayoutManager
public static int chooseSize(int spec, int desired, int min) {
   
    final int mode = View.MeasureSpec.getMode(spec);
    final int size = View.MeasureSpec.getSize(spec);
    switch (mode) {
   
        case View.MeasureSpec.EXACTLY:
            return size;
        case View.MeasureSpec.AT_MOST:
            return Math.min(size, Math.max(desired, min));
        case View.MeasureSpec.UNSPECIFIED:
        default:
            return Math.max(desired, min);
    }
}

默认测量,直接是利用View.MeasureSpec测算,所以可能会出现各种问题

[1.1.2] dispatchLayoutStep1

测量的状态有几个:

  1. STEP_START: 初始状态
  2. STEP_LAYOUT: 第一阶段preLayout完成,等待第二阶段真正的布局layout
  3. STEP_ANIMATIONS: 第二阶段layout完毕,等待第三阶段开始处理动画

dispatchLayoutStep1 主要工作:

  1. 处理adapter的数据更新
  2. 决定需要运行的动画
  3. 保存当前views的信息
  4. 若需要,提前layout和保存信息
private void dispatchLayoutStep1() {
   
    mState.assertLayoutStep(State.STEP_START);
    processAdapterUpdatesAndSetAnimationFlags();
    if (mState.mRunSimpleAnimations) {
   
        // Step:保存未删除的ViewHolder的信息,进行pre_layout
        in
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值