RecyclerView组件

  1. Adapter
    提供数据以及数据变更通知
  2. LayoutManager
    布局管理,负责测绘、指定item位置、item回收、
  3. ItemAnimator
    Item变更动画

关键实现

1、ViewHolder复用

三层缓存
第一层:Recycler中的mCachedViews

第二层:ViewCacheExtension 由开发者实现的缓存策略,可通过setViewCacheExtension设置到RecyclerView中

第三层:RecycledViewPool中的mScrap 可在多个RecyclerView共享的View缓存池

Scrapped View(废弃的View):一个View虽然仍附属在它的父视图RecyclerView上,但是已经被标记为已使用或已迁移。这样的View就叫做Scrapped View。

获取一个View的过程

//RecyclerView.Recyclerfinal ArrayList mAttachedScrap = new ArrayList<>();        private ArrayList mChangedScrap = null;        final ArrayList mCachedViews = new ArrayList();        private final List                mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);//所有获取View调用的入口public View getViewForPosition(int position) {            //调用内部实现            //参数 position 获取一个View的位置            //     false   不进行预检操作  dryrun            return getViewForPosition(position, false);        }View getViewForPosition(int position, boolean dryRun) {    //参数检查    ...    //如果处于提前布局状态,尝试从mChangedScrap列表寻找    if (mState.isPreLayout()) {                holder = getChangedScrapViewForPosition(position);                fromScrap = holder != null;            }     //如果没找到,则从mAttachedScrap列表中寻找     holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);     //如果没找到,或者找到的ViewHolder和OffsetPosition不匹     //配,则重新计算OffsetPosition。并且根据stable ids在     //mAttachedScrap中寻找(如果mAdapter存在stableIds)。      if (mAdapter.hasStableIds()) {                    holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);                    if (holder != null) {                        // update position                        holder.mPosition = offsetPosition;                        fromScrap = true;                    }                }    //如果还找不到,则从mViewCacheExtension中寻找,    //前提是开发者为RecyclerView设置了ViewCacheExtension    if (holder == null && mViewCacheExtension != null) {                    // We are NOT sending the offsetPosition because LayoutManager does not                    // know it.                    final View view = mViewCacheExtension                            .getViewForPositionAndType(this, position, type);    ....    ....    //如果没有设置ViewCacheExtension,肯定还是找不到的,    //那么就会从RecycledViewPool中寻找。    //因为RecycledViewPool是多个RecyclerView共享的,如果找到合适的ViewHolder,要先更新需要显示的信息。    holder = getRecycledViewPool().getRecycledView(type);    ...    ...    //最后一层缓存也找不到的话,只能新建了    holder = mAdapter.createViewHolder(RecyclerView.this, type);     ...//填充数据 mAdapter.bindViewHolder(holder, offsetPosition);     ...     return holder.itemView; }

2、数据变更通知(观察者模式)

观察者AdapterDataObserver,具体实现为RecyclerViewDataObserver,当数据源发生变更时,及时响应界面变化

public static abstract class AdapterDataObserver {        public void onChanged() {            // Do nothing        }        public void onItemRangeChanged(int positionStart, int itemCount) {            // do nothing        }        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {            // fallback to onItemRangeChanged(positionStart, itemCount) if app            // does not override this method.            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        }    }

被观察者AdapterDataObservable,内部持有观察者AdapterDataObserver集合

static class AdapterDataObservable extends Observable {        public boolean hasObservers() {            return !mObservers.isEmpty();        }        public void notifyChanged() {            // since onChanged() is implemented by the app, it could do anything, including            // removing itself from {@link mObservers} - and that could cause problems if            // an iterator is used on the ArrayList {@link mObservers}.            // to avoid such problems, just march thru the list in the reverse order.            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, Object payload) {            // since onItemRangeChanged() is implemented by the app, it could do anything, including            // removing itself from {@link mObservers} - and that could cause problems if            // an iterator is used on the ArrayList {@link mObservers}.            // to avoid such problems, just march thru the list in the reverse order.            for (int i = mObservers.size() - 1; i >= 0; i--) {                mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);            }        }        public void notifyItemRangeInserted(int positionStart, int itemCount) {            // since onItemRangeInserted() is implemented by the app, it could do anything,            // including removing itself from {@link mObservers} - and that could cause problems if            // an iterator is used on the ArrayList {@link mObservers}.            // to avoid such problems, just march thru the list in the reverse order.            for (int i = mObservers.size() - 1; i >= 0; i--) {                mObservers.get(i).onItemRangeInserted(positionStart, itemCount);            }        }        public void notifyItemRangeRemoved(int positionStart, int itemCount) {            // since onItemRangeRemoved() is implemented by the app, it could do anything, including            // removing itself from {@link mObservers} - and that could cause problems if            // an iterator is used on the ArrayList {@link mObservers}.            // to avoid such problems, just march thru the list in the reverse order.            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);            }        }    }

Adapter内部持有AdapterDataObservable对象。
当我们为RecyclerView设置Adapter时,会向Adapter注册一个观察者RecyclerViewDataObserver。

adapter.registerAdapterDataObserver(mObserver);

当数据变更时,调用notify**方法时,Adapter内部的被观察者会遍历通知已经注册的观察者的对应方法,这时界面就会响应变更。

3、多种布局类型展示
基类LayoutManager
子类有LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager

测量入口:RecyclerView的onMeasure方法

//mAutoMeasure代表是否是由已经存在的LayoutManager去执行测绘工作。//当使用自定义的LayoutManager时,需要设置此值为falseif (mLayout.mAutoMeasure) {            final int widthMode = MeasureSpec.getMode(widthSpec);            final int heightMode = MeasureSpec.getMode(heightSpec);            //如果RecyclerView的测量模式为EXACTLY(准确模式)            //则不必依赖子视图的尺寸来确定RecyclerView本身的高度            //子视图的测量工作会延迟到onLayout方法中            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) {                dispatchLayoutStep1();            }            // set dimensions in 2nd step. Pre-layout should happen with old dimensions for            // consistency            mLayout.setMeasureSpecs(widthSpec, heightSpec);            mState.mIsMeasuring = true;            //此方法会真正执行LayoutManager的测量布局操作            dispatchLayoutStep2();            .....            .....
 /**     * The second layout step where we do the actual layout of the views for the final state.     * This step might be run multiple times if necessary (e.g. measure).     */    private void dispatchLayoutStep2() {    ...    ...    //mLayout即RecyclerView设置的LayoutManager    mLayout.onLayoutChildren(mRecycler, mState);    ...    ...   }
//LayoutManager的这个方法是一个空实现,具体布局操作由子类决定//当自定义LayoutManager时,这是必须要实现的一个方法public void onLayoutChildren(Recycler recycler, State state) {            Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");        }
//这里以LinearLayoutManager为例,说明布局过程//通过注释可以很好的理解测量和布局算法流程@Override    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {        // layout algorithm:        // 1) by checking children and other variables, find an anchor coordinate and an anchor        //  item position.        // 2) fill towards start, stacking from bottom        // 3) fill towards end, stacking from top        // 4) scroll to fulfill requirements like stack from bottom.        ...        ...        //第一步,通过检查子视图和其他变量,找出锚点坐标和item位置锚点        //第二步,从底部向起始位置填充        //第三步,从顶部向末尾位置填充        //第四步,滚动要满足要求的位置,例如当从底部填充时        ...        ...        //当锚点计算完成后,就开始填充view        //LayoutManager的三个子类都有这个填充方法,但是不同的        //的布局类型,此方法会有不同的实现        fill(recycler, mLayoutState, state, false);

4、Item变更动画

5、其他

notifyDataSetChanged发生了什么

1、为页面中存在的ViewHolder和Recycler中mCachesView中缓存的ViewHolder添加一个标志:ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN

2、检查有没有等待执行的更新操作(add/remove/update/move)。如果没有的话,并且当前没有处于布局状态,并且没有设置禁止LayoutFrozen,调用requestLayout()方法重新布局。

3、如果有等待执行的更新操作怎么办?
不允许刷新

notifyItemInserted发生了什么

1、RecyclerView把Item的插入、删除、移动、变更抽象成了一个个命令UpdateOp,并在AdapterHelper中维护了一个命令池。当插入一条Item时,向更新的命令队列中添加一个“ADD”命令。

mPendingUpdates.add(obtainUpdateOp(UpdateOp.ADD, positionStart, itemCount, null));

2、然后会触发更新处理器,来处理新的命令。

3、RecyclerView会启动一个Runnable来处理等待执行的命令。

    /**     * Note: this Runnable is only ever posted if:     * 1) We've been through first layout     * 2) We know we have a fixed size (mHasFixedSize)     * 3) We're attached     */    private final Runnable mUpdateChildViewsRunnable = new Runnable() {        public void run() {            if (!mFirstLayoutComplete || isLayoutRequested()) {                // a layout request will happen, we should not do layout here.                return;            }            if (mLayoutFrozen) {                mLayoutRequestEaten = true;                return; //we'll process updates when ice age ends.            }            consumePendingUpdateOperations();        }    };

但是我们在单步调试的时候,发现条件isLayoutRequested()为true,所以这个Runnable什么都没干,直接返回了。
????

既然isLayoutRequested()为true,说明接下来会重新布局,可能执行更新的地方放在了layout中。
我们在dispatchLayout()方法中的第二步dispatchLayoutStep1();发现了processAdapterUpdatesAndSetAnimationFlags()方法;
该方法主要做了两件事:第一,根据操作类型和数量更新Adapter中Item的位置(例,插入一个新的Item,原列表中Item的Position要依次加一)。第二,设置RecyclerView的状态mState.mRunSimpleAnimations和mState.mRunPredictiveAnimations为true。

更多相关文章

  1. Android如何读写CSV文件方法示例
  2. Android(安卓)Handler机制3--SystemClock类
  3. Android封装SDK生成Jar包以及混淆的方法
  4. Android(安卓)架构师之路22 响应式编程RxJava 整体变换
  5. 【Android基础】(1)四大核心组件之Activity
  6. Android(安卓)8.0 启动Service适配(Not allowed to start servic
  7. 【Android】 Activity Lifecycle
  8. Android(安卓)RecyclerView 二级列表实现
  9. android实现点击按钮切换不同的fragment布局

随机推荐

  1. Android修改Eclipse 中的Default debug k
  2. Android环境安装
  3. android布局的一些知识
  4. Android中RelativeLayout各个属性的含义
  5. android:textAppearance是什么意思
  6. 源码解析Android中AsyncTask的工作原理
  7. pandaboard ES学习之旅——4 Android源代
  8. Android(安卓)多个Activity选项卡实现
  9. android 电容屏(二):驱动调试之基本概念篇
  10. Android中创建对话框