zoukankan      html  css  js  c++  java
  • RecyclerView 介绍 02 – 重要概念

    几个概念

    1. RecyclerView是一个ViewGroup;
    2. LayoutManager控制RecyclerView的ChildView的布局显示,childview由Recycler提供以及管理;
    3. Recycler具有两级缓存,Scrap和RecycledViewPool,通过Detach以及Remove,对Viewholder进行转移以及状态改变;
    4. RecycledViewPool可以由多个RecyclerView共享;
    5. ViewHolder具有多种状态标记;

    关于Recycler

    Scrap中的ViewHolder,不用通过Adapter重新处理,只需要attach后回到LayoutManager就可以重用。

    RecycledViewPool中的ViewHolder,数据往往是错误的,则需要通过Adapter重新绑定正确的数据后在回到LayoutManager。

    当LayoutManager需要一个新的View时,Recycler会行检查scrap中是否有合适的ViewHolder,如果有直接返回给LayoutManager使用;如果没有,就需要从Pool里面寻找,然后右Adapter重新绑定数据后,返回到LayoutManager;如果pool还是没有,就需要由Adapter创建一个新的Viewholder。见如下代码:

    1. View getViewForPosition(int position, boolean dryRun) {
    2.             if (position < 0 || position >= mState.getItemCount()) {
    3.                 throw new IndexOutOfBoundsException("Invalid item position " + position
    4.                         + "(" + position + "). Item count:" + mState.getItemCount());
    5.             }
    6.             boolean fromScrap = false;
    7.             ViewHolder holder = null;
    8.             // 0) If there is a changed scrap, try to find from there
    9.             if (mState.isPreLayout()) {
    10.                 holder = getChangedScrapViewForPosition(position);
    11.                 fromScrap = holder != null;
    12.             }
    13.             // 1) Find from scrap by position
    14.             if (holder == null) {
    15.                 holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
    16.                 if (holder != null) {
    17.                     if (!validateViewHolderForOffsetPosition(holder)) {
    18.                         // recycle this scrap
    19.                         if (!dryRun) {
    20.                             // we would like to recycle this but need to make sure it is not used by
    21.                             // animation logic etc.
    22.                             holder.addFlags(ViewHolder.FLAG_INVALID);
    23.                             if (holder.isScrap()) {
    24.                                 removeDetachedView(holder.itemView, false);
    25.                                 holder.unScrap();
    26.                             } else if (holder.wasReturnedFromScrap()) {
    27.                                 holder.clearReturnedFromScrapFlag();
    28.                             }
    29.                             recycleViewHolderInternal(holder);
    30.                         }
    31.                         holder = null;
    32.                     } else {
    33.                         fromScrap = true;
    34.                     }
    35.                 }
    36.             }
    37.             if (holder == null) {
    38.                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
    39.                 if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
    40.                     throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
    41.                             + "position " + position + "(offset:" + offsetPosition + ")."
    42.                             + "state:" + mState.getItemCount());
    43.                 }
    44.  
    45.                 final int type = mAdapter.getItemViewType(offsetPosition);
    46.                 // 2) Find from scrap via stable ids, if exists
    47.                 if (mAdapter.hasStableIds()) {
    48.                     holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
    49.                     if (holder != null) {
    50.                         // update position
    51.                         holder.mPosition = offsetPosition;
    52.                         fromScrap = true;
    53.                     }
    54.                 }
    55.                 if (holder == null && mViewCacheExtension != null) {
    56.                     // We are NOT sending the offsetPosition because LayoutManager does not
    57.                     // know it.
    58.                     final View view = mViewCacheExtension
    59.                             .getViewForPositionAndType(this, position, type);
    60.                     if (view != null) {
    61.                         holder = getChildViewHolder(view);
    62.                         if (holder == null) {
    63.                             throw new IllegalArgumentException("getViewForPositionAndType returned"
    64.                                     + " a view which does not have a ViewHolder");
    65.                         } else if (holder.shouldIgnore()) {
    66.                             throw new IllegalArgumentException("getViewForPositionAndType returned"
    67.                                     + " a view that is ignored. You must call stopIgnoring before"
    68.                                     + " returning this view.");
    69.                         }
    70.                     }
    71.                 }
    72.                 if (holder == null) { // fallback to recycler
    73.                     // try recycler.
    74.                     // Head to the shared pool.
    75.                     if (DEBUG) {
    76.                         Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
    77.                                 + "pool");
    78.                     }
    79.                     holder = getRecycledViewPool()
    80.                             .getRecycledView(mAdapter.getItemViewType(offsetPosition));
    81.                     if (holder != null) {
    82.                         holder.resetInternal();
    83.                         if (FORCE_INVALIDATE_DISPLAY_LIST) {
    84.                             invalidateDisplayListInt(holder);
    85.                         }
    86.                     }
    87.                 }
    88.                 if (holder == null) {
    89.                     holder = mAdapter.createViewHolder(RecyclerView.this,
    90.                             mAdapter.getItemViewType(offsetPosition));
    91.                     if (DEBUG) {
    92.                         Log.d(TAG, "getViewForPosition created new ViewHolder");
    93.                     }
    94.                 }
    95.             }
    96.             boolean bound = false;
    97.             if (mState.isPreLayout() && holder.isBound()) {
    98.                 // do not update unless we absolutely have to.
    99.                 holder.mPreLayoutPosition = position;
    100.             } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
    101.                 if (DEBUG && holder.isRemoved()) {
    102.                     throw new IllegalStateException("Removed holder should be bound and it should"
    103.                             + " come here only in pre-layout. Holder: " + holder);
    104.                 }
    105.                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
    106.                 mAdapter.bindViewHolder(holder, offsetPosition);
    107.                 attachAccessibilityDelegate(holder.itemView);
    108.                 bound = true;
    109.                 if (mState.isPreLayout()) {
    110.                     holder.mPreLayoutPosition = position;
    111.                 }
    112.             }
    113.  
    114.             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
    115.             final LayoutParams rvLayoutParams;
    116.             if (lp == null) {
    117.                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
    118.                 holder.itemView.setLayoutParams(rvLayoutParams);
    119.             } else if (!checkLayoutParams(lp)) {
    120.                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
    121.                 holder.itemView.setLayoutParams(rvLayoutParams);
    122.             } else {
    123.                 rvLayoutParams = (LayoutParams) lp;
    124.             }
    125.             rvLayoutParams.mViewHolder = holder;
    126.             rvLayoutParams.mPendingInvalidate = fromScrap && bound;
    127.             return holder.itemView;
    128.         }

    关于ViewHolder

    在RecyclerView里面,view是有多重状态的,各种状态在ViewHolder里面定义。看看下面的代码:

    1. public static abstract class ViewHolder {
    2.       public final View itemView;
    3.       int mPosition = NO_POSITION;
    4.       int mOldPosition = NO_POSITION;
    5.       long mItemId = NO_ID;
    6.       int mItemViewType = INVALID_TYPE;
    7.       int mPreLayoutPosition = NO_POSITION;
    8.  
    9.       // The item that this holder is shadowing during an item change event/animation
    10.       ViewHolder mShadowedHolder = null;
    11.       // The item that is shadowing this holder during an item change event/animation
    12.       ViewHolder mShadowingHolder = null;
    13.  
    14.       /**
    15.        * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
    16.        * are all valid.
    17.        */
    18.       static final int FLAG_BOUND = 1 << 0;
    19.  
    20.       /**
    21.        * The data this ViewHolder's view reflects is stale and needs to be rebound
    22.        * by the adapter. mPosition and mItemId are consistent.
    23.        */
    24.       static final int FLAG_UPDATE = 1 << 1;
    25.  
    26.       /**
    27.        * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
    28.        * are not to be trusted and may no longer match the item view type.
    29.        * This ViewHolder must be fully rebound to different data.
    30.        */
    31.       static final int FLAG_INVALID = 1 << 2;
    32.  
    33.       /**
    34.        * This ViewHolder points at data that represents an item previously removed from the
    35.        * data set. Its view may still be used for things like outgoing animations.
    36.        */
    37.       static final int FLAG_REMOVED = 1 << 3;
    38.  
    39.       /**
    40.        * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
    41.        * and is intended to keep views around during animations.
    42.        */
    43.       static final int FLAG_NOT_RECYCLABLE = 1 << 4;
    44.  
    45.       /**
    46.        * This ViewHolder is returned from scrap which means we are expecting an addView call
    47.        * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
    48.        * the end of the layout pass and then recycled by RecyclerView if it is not added back to
    49.        * the RecyclerView.
    50.        */
    51.       static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
    52.  
    53.       /**
    54.        * This ViewHolder's contents have changed. This flag is used as an indication that
    55.        * change animations may be used, if supported by the ItemAnimator.
    56.        */
    57.       static final int FLAG_CHANGED = 1 << 6;
    58.  
    59.       /**
    60.        * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
    61.        * it unless LayoutManager is replaced.
    62.        * It is still fully visible to the LayoutManager.
    63.        */
    64.       static final int FLAG_IGNORE = 1 << 7;

    ------EOF----------

  • 相关阅读:
    深入理解java:2.3.1. 并发编程concurrent包 之Atomic原子操作(循环CAS)
    深入理解java:2.3. 并发编程 java.util.concurrent包
    深入理解java:2.2. 同步锁Synchronized及其实现原理
    深入理解java:2.1. volatile的使用及其原理
    深入理解java:2. 多线程机制
    深入理解java:1.3.2 JVM监控与调优
    深入理解java:1.3.1 JVM内存区域的划分(运行时数据区)
    深入理解java:1.3. 垃圾收集
    深入理解java:1.2. 字节码执行引擎
    线程的等待与唤醒,实现if...else里面的值交互依次输出
  • 原文地址:https://www.cnblogs.com/halzhang/p/4445145.html
Copyright © 2011-2022 走看看