RecyclerView源码讲解系列(一)总览

​从今天开始RecyclerView源码讲解系列正式开始更新,每周一篇。欢迎大家持续关注。
RecyclerView是Android中十分重要的视图组件,可以说基本每个APP的主页面都用到了RecyclerView,由此可见其重要性。那么问题来了,如何对RecyclerView做性能优化、并且封装成一个简单易用且功能强大的RecyclerView。那么首先第一步就是先了解RecyclerView,对它的整体设计有一个总览的概念。由此诞生了RecyclerView源码系列讲解(主要注重整体流程的讲解),后续也会去写RecyclerView性能优化系列 和 RecyclerView 封装系列文章。
RecyclerView源码讲解系列将分为以下几篇:

  1. 总览(主要介绍文章目录和RecyclerView的重要成员)9.25
  2. RecyclerView的measure和layout流程(上)10.2
  3. RecyclerView的measure和layout流程(下) 10.9
  4. RecyclerView的Scroll和Fling流程 10.16
  5. RecyclerView的四级缓存 10.23
  6. RecyclerView的局部更新原理 10.30
    欢迎大家关注微信公众号:潇洒哥讲Android
    在这里插入图片描述

为了节约时间,后续文章将第一时间在微信公众号发布。CSDN延迟发布
本篇的主要内容是让大家对于RecyclerView有一个整体的认知和了解。认识一下RecyclerView家族的主要成员(静态内部类)

(一)Adapter

Adapter的作用是提供一个从数据->视图的映射关系,当数据发生变化的时候也会及时通知观察者,更新数据->视图的映射。

public abstract static class Adapter<VH extends ViewHolder> {
         //存储注册的观察者对象,当数据源发生改变的时候用来去通知观察者
        private final AdapterDataObservable mObservable = new AdapterDataObservable();
        
        //根据传入鹅ViewType来创建我们想要的ViewHolder,这个ViewHolder必须是VH(泛型)的子类
        public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
        //给ViewHolder绑定数据,调用ViewHolder的bind方法
        public abstract void onBindViewHolder(@NonNull VH holder, int position);
        
        //onCreateViewHolder方法中的ViewType参数就取自该方法,我们可以重写该方法。
        //根据不同的model,返回不同的ViewType,再创建不同的ViewHolder。
        //当我们对Adapter进行封装的时候,这里可以是封装的重点区域。让其可以根据不同的model
        //自动映射并创建不同的ViewHolder
        public int getItemViewType(int position) {
            return 0;
        }
        
        //下面的方法都是用来通知观察者数据发生变化,视图需要更新的方法
        //推荐使用notifyItem...系列方法,性能会更佳。在局部更新原理中会讲到原因
        public final void notifyDataSetChanged() {
            mObservable.notifyChanged();
        }
         
        public final void notifyItemChanged(int position) {
            mObservable.notifyItemRangeChanged(position, 1);
        }
        //...... 代表还有很多其他代码,这里只讲解比较重要和常用的方法
    }

(二)ViewHolder

ViewHolder可以看作是View的包装类,它会持有一个View,并且保存和这个View相关的参数。里面有一个比较重要的成员变量是flags,该变量会保存当前ViewHolder的状态。

public abstract static class ViewHolder {
        //ViewHolder持有的View
        public final View itemView;
        //持有嵌套鹅RecyclerView
        WeakReference<RecyclerView> mNestedRecyclerView;
        //当前的对应Adapter中的位置
        int mPosition = NO_POSITION;
        //老位置
        int mOldPosition = NO_POSITION;
        long mItemId = NO_ID;
        //对应的ItemType
        int mItemViewType = INVALID_TYPE;
        int mPreLayoutPosition = NO_POSITION;
        //记录当前ViewHolder的状态,例如:数据是否失效,是否需要bind,是否回收
         int mFlags;
        //被添加到了哪个RecyclerView上
        RecyclerView mOwnerRecyclerView;
}

(三)LayoutManager

RecyclerView会把measure、layout、scroll行为委托给LayoutManager管理。这也是为什么我们使用不同的LayoutManager的子类可以得到不同的布局效果。

  //LayoutManager 主要关注下面四个方法
  public abstract static class LayoutManager {
        //...//
        //用来测RecyclerView的大小
        public void onMeasure(@NonNull Recycler recycler, @NonNull State state, int widthSpec,
                int heightSpec) {
        }
        //用来对RecyclerView的子View进行布局
        public void onLayoutChildren(Recycler recycler, State state) {
        }
        //用于处理RecyclerView垂直方向的滑动
        public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
            return 0;
        }
        //用于处理RecyclerView的水平方向滑动
         public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
            return 0;
        }
        //...//
}

(四)Recycler

Recycler主要负责对ViewHolder进行回收和缓存。RecycelrView四级缓存机制就是用它来实现的。

public final class Recycler {
      //一级缓存,存储屏幕上可见的ViewHolder
        final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
        //二级缓存,缓存当前不可见,滚动到下一个就可见并且不需要执行bind方法的ViewHolder
        final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
         //三级缓存,自定义缓存
        private ViewCacheExtension mViewCacheExtension;
        //四级缓存,对象池
        RecycledViewPool mRecyclerPool;
}

(五)RecyclerViewPool

RecyclerViewPool是四级缓存中的最后一级,是一个缓存ViewHolder的对象池,默认每个ViewType对应的ViewHolder最多缓存5个。

public static class RecycledViewPool {
        private static final int DEFAULT_MAX_SCRAP = 5;
        static class ScrapData {
            final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
            int mMaxScrap = DEFAULT_MAX_SCRAP;
            long mCreateRunningAverageNs = 0;
            long mBindRunningAverageNs = 0;
        }
        //SparseArray是轻量级的HashMap,key只能为int。
        //存放的是每个ViewType对应的ViewHolder
        SparseArray<ScrapData> mScrap = new SparseArray<>();
}

(六)ViewCacheExtension

ViewCacheExtension是四级缓存中的第三级,这是一个抽象类,里面只有一个getViewForPositionAndType方法,我们可以写一个该类的实现类,并把对象设置给RecyclerView,达到插入我们自己缓存策略的目的。

public abstract static class ViewCacheExtension {
        public abstract View getViewForPositionAndType(@NonNull Recycler recycler, int position,
                int type);
    }

(七)ViewFlinger

ViewFlinger负责处理当滑动列表,并且手指离开屏幕,列表依旧滚动(称为Fling状态)的行为。

 class ViewFlinger implements Runnable {
        //...//
        //封装了具有超出滚动操作范围的滚动功能。Scroller 的直接替代品。
        OverScroller mOverScroller;
        //插值器,用于计算每帧需要滚动的距离,默认会用线性插值器,如果想让RecyclerView的Fling有阻尼的效果,可以采用其他插值器
        Interpolator mInterpolator = sQuinticInterpolator;ViewFlinger() {
            mOverScroller = new OverScroller(getContext(), sQuinticInterpolator);
        }@Override
        public void run() {
            //用来执行每帧的滚动逻辑
        }
        //...//
}

​(八)State

State会保存当前 RecyclerView 状态的有用信息,比如目标滚动位置或视图焦点。状态对象还可以保存由资源 ID 标识的任意数据。
很多时候,RecyclerView 组件需要在彼此之间传递信息。为了在组件之间提供良好定义的数据总线,RecyclerView 将相同的 State 对象传递给组件回调,这些组件可以使用它来交换数据。
如果实现自定义组件,则可以使用 State 的 put/get/remove 方法在组件之间传递数据,而无需管理它们的生命周期。

 public static class State {
        static final int STEP_START = 1;
        static final int STEP_LAYOUT = 1 << 1;
        static final int STEP_ANIMATIONS = 1 << 2;//记录要滚动到的位置
        int mTargetPosition = RecyclerView.NO_POSITION;
        //保存由资源 ID 标识的任意数据 key为id,value是data
        private SparseArray<Object> mData;/**
         * adapter在先前布局中的Item数。
         */
        int mPreviousLayoutItemCount = 0;/**
         * 上一次布局中删除的不可见Item的数量。
         */
        int mDeletedInvisibleItemCountSincePreviousLayout = 0;@IntDef(flag = true, value = {
                STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
        })
        @Retention(RetentionPolicy.SOURCE)
        @interface LayoutState {}
        //记录当前Laytout走到了第几步(RecyclerView Layout共三步)
        @LayoutState
        int mLayoutStep = STEP_START;/**
         * adapter中Item的数量
         */
        int mItemCount = 0;
        //标志RecyclerView的结构是否改变
        boolean mStructureChanged = false;/**
         * 当前RecyclerView是否处于pre-layout状态
         */
        boolean mInPreLayout = false;
        
        boolean mTrackOldChangeHolders = false;
        //RecyclerView是否正在进行measure
        boolean mIsMeasuring = false;
        //是否运行简单的动画
        boolean mRunSimpleAnimations = false;
        //是否运行预测动画
        boolean mRunPredictiveAnimations = false;/**
         * 记录RecyclerView中持有焦点鹅位置和ItemId
         */
        int mFocusedItemPosition;
        long mFocusedItemId;
        // 当一个子孩子有焦点时,记录它的id,看看我们是否可以直接请求焦点在那个上
        int mFocusedSubChildId;
        //记录当前剩余的滚动距离
        int mRemainingScrollHorizontal;
        int mRemainingScrollVertical;
}

(九)ItemAnimator

ItemAnimator主要负责每个Item的动画展示效果,当Item被添加、删除、移动、数据变化的时候。当我们想要使用自定义动画的时候就可以继承该类实现子类,再通过setItemAnimator(RecyclerView.ItemAnimator)方法设置给RecyclerView。默认使用DefaultItemAnimator。

(十)EdgeEffectFactory

EdgeEffectFactory主要是让我们可以自定义当滑动到边界继续滑动时边界动画的效果。

public static class EdgeEffectFactory {
        //创建一个EdgeEffect用于显示边界效果
        protected @NonNull EdgeEffect createEdgeEffect(@NonNull RecyclerView view,
                @EdgeDirection int direction) {
            return new EdgeEffect(view.getContext());
        }
    }

(十一)ItemDecoration

ItemDecoration 可以让应用程序向adpater数据集中的特定项目视图添加特殊的绘图和布局偏移。 主要用在在Item突出显示、Item之间视觉分组边界绘制分隔线。

public abstract static class ItemDecoration {
        /**
         * 提供在 RecyclerView 的 Canvas 中绘制任何适当的装饰。 
         * 通过此方法绘制的任何内容都将在绘制Item视图之前绘制,
         * 因此将出现在视图下方。
         *
         * @param c Canvas to draw into
         * @param parent RecyclerView this ItemDecoration is drawing into
         * @param state The current state of RecyclerView
         */
        public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull State state) {
            onDraw(c, parent);
        }/**
         *这个将在Item绘制后,再绘制。会出现在Item绘制d鹅上方
         *
         * @param c Canvas to draw into
         * @param parent RecyclerView this ItemDecoration is drawing into
         * @param state The current state of RecyclerView.
         */
        public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent,
                @NonNull State state) {
            onDrawOver(c, parent);
        }//我们经常使用该方法去设置Item之间的边界
        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
                @NonNull RecyclerView parent, @NonNull State state) {
            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
                    parent);
        }
    }

(十二)LayoutParams

RecyclerView.LayoutParams继承自ViewGroup.MarginLayoutParams,添加了一些在ViewHolder中需要用到的参数和方法。这个LayoutParams将被使用在ViewHolder中的View身上。每个自定义LayoutManager可以再实现一个该LayoutParams的子类,用于存储每个LayoutManager需要的特定信息在ViewHolder身上。

public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
        //绑定的ViewHolder
        ViewHolder mViewHolder;
        //ViewHolder的边界
        final Rect mDecorInsets = new Rect();
        //如果视图在与 RV 分离时被绑定,则标志设置为 true。
        // 在这种情况下,我们需要在添加视图后手动调用 invalidate 
        // 以保证通过 View 层次结构填充失效
        boolean mPendingInvalidate = false;/**
         *  如果视图应该更新其内容,则为 true
         */
        public boolean viewNeedsUpdate() {
            return mViewHolder.needsUpdate();
        }/**
         * 如果ViewHolder已经Invalid则返回true
         */
        public boolean isViewInvalid() {
            return mViewHolder.isInvalid();
        }/*
         * 如果这个ViewHolder对应的Item已经在Adapter中的数据集中被删除,返回true
         */
        public boolean isItemRemoved() {
            return mViewHolder.isRemoved();
        }/**
         * 如果视图对应的项目在数据集中已更改,则为 true
         */
        public boolean isItemChanged() {
            return mViewHolder.isUpdated();
        }/**
         * 在最新的布局中,这个View在Adapter中布局位置
         */
        public int getViewLayoutPosition() {
            return mViewHolder.getLayoutPosition();
        }/**
         * 返回当前ViewHolder对应Adapter中的位置
         */
        public int getViewAdapterPosition() {
            return mViewHolder.getAdapterPosition();
        }
    }

(十三)AdapterDataObservable 和 AdapterDataObserver

典型的观察者模式。
Adapter持有AdapterDataObservable用于保存注册了的观察者对象AdapterDataObserver。当Adapter中的数据发生变化时,会通过AdapterDataObservable告知AdapterDataObserver。

static class AdapterDataObservable extends Observable<AdapterDataObserver> {
        //是否存在订阅的观察者
        public boolean hasObservers() {
            return !mObservers.isEmpty();
        }
        //下面的方法都是通知已经订阅的观察者数据集发生变化
        public void notifyChanged() {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }public void notifyItemRangeChanged(int positionStart, int itemCount) {
            notifyItemRangeChanged(positionStart, itemCount, null);
        }public void notifyItemRangeChanged(int positionStart, int itemCount,
                @Nullable Object payload) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
            }
        }public void notifyItemRangeInserted(int positionStart, int itemCount) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
            }
        }public void notifyItemRangeRemoved(int positionStart, int itemCount) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
            }
        }public void notifyItemMoved(int fromPosition, int toPosition) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
            }
        }
    }
//想要订阅被观察者就继承该类并实现响应的通知方法,当被观察者数据发生变化时,相应的方法会被调用。
public abstract static class AdapterDataObserver {
        public void onChanged() {
            // Do nothing
        }public void onItemRangeChanged(int positionStart, int itemCount) {
            // do nothing
        }public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
            onItemRangeChanged(positionStart, itemCount);
        }public void onItemRangeInserted(int positionStart, int itemCount) {
            // do nothing
        }public void onItemRangeRemoved(int positionStart, int itemCount) {
            // do nothing
        }public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            // do nothing
        }
    }

下一篇:RecyclerView的measure和layout流程(上)将于10.2日在微信公众号:"潇洒哥讲Android"准时发布,欢迎大家及时关注。
在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值