RecyclerView 介紹 02 – 重要概念

銳湃發表於2015-12-04

幾個概念

  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。見如下程式碼:


View getViewForPosition(int position, boolean dryRun) {
            if (position < 0 || position >= mState.getItemCount()) {
                throw new IndexOutOfBoundsException("Invalid item position " + position
                        + "(" + position + "). Item count:" + mState.getItemCount());
            }
            boolean fromScrap = false;
            ViewHolder holder = null;
            // 0) If there is a changed scrap, try to find from there
            if (mState.isPreLayout()) {
                holder = getChangedScrapViewForPosition(position);
                fromScrap = holder != null;
            }
            // 1) Find from scrap by position
            if (holder == null) {
                holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
                if (holder != null) {
                    if (!validateViewHolderForOffsetPosition(holder)) {
                        // recycle this scrap
                        if (!dryRun) {
                            // we would like to recycle this but need to make sure it is not used by
                            // animation logic etc.
                            holder.addFlags(ViewHolder.FLAG_INVALID);
                            if (holder.isScrap()) {
                                removeDetachedView(holder.itemView, false);
                                holder.unScrap();
                            } else if (holder.wasReturnedFromScrap()) {
                                holder.clearReturnedFromScrapFlag();
                            }
                            recycleViewHolderInternal(holder);
                        }
                        holder = null;
                    } else {
                        fromScrap = true;
                    }
                }
            }
            if (holder == null) {
                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
                    throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
                            + "position " + position + "(offset:" + offsetPosition + ")."
                            + "state:" + mState.getItemCount());
                }
 
                final int type = mAdapter.getItemViewType(offsetPosition);
                // 2) Find from scrap via stable ids, if exists
                if (mAdapter.hasStableIds()) {
                    holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
                    if (holder != null) {
                        // update position
                        holder.mPosition = offsetPosition;
                        fromScrap = true;
                    }
                }
                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);
                    if (view != null) {
                        holder = getChildViewHolder(view);
                        if (holder == null) {
                            throw new IllegalArgumentException("getViewForPositionAndType returned"
                                    + " a view which does not have a ViewHolder");
                        } else if (holder.shouldIgnore()) {
                            throw new IllegalArgumentException("getViewForPositionAndType returned"
                                    + " a view that is ignored. You must call stopIgnoring before"
                                    + " returning this view.");
                        }
                    }
                }
                if (holder == null) { // fallback to recycler
                    // try recycler.
                    // Head to the shared pool.
                    if (DEBUG) {
                        Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
                                + "pool");
                    }
                    holder = getRecycledViewPool()
                            .getRecycledView(mAdapter.getItemViewType(offsetPosition));
                    if (holder != null) {
                        holder.resetInternal();
                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
                            invalidateDisplayListInt(holder);
                        }
                    }
                }
                if (holder == null) {
                    holder = mAdapter.createViewHolder(RecyclerView.this,
                            mAdapter.getItemViewType(offsetPosition));
                    if (DEBUG) {
                        Log.d(TAG, "getViewForPosition created new ViewHolder");
                    }
                }
            }
            boolean bound = false;
            if (mState.isPreLayout() && holder.isBound()) {
                // do not update unless we absolutely have to.
                holder.mPreLayoutPosition = position;
            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
                if (DEBUG && holder.isRemoved()) {
                    throw new IllegalStateException("Removed holder should be bound and it should"
                            + " come here only in pre-layout. Holder: " + holder);
                }
                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                mAdapter.bindViewHolder(holder, offsetPosition);
                attachAccessibilityDelegate(holder.itemView);
                bound = true;
                if (mState.isPreLayout()) {
                    holder.mPreLayoutPosition = position;
                }
            }
 
            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
            final LayoutParams rvLayoutParams;
            if (lp == null) {
                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
                holder.itemView.setLayoutParams(rvLayoutParams);
            } else if (!checkLayoutParams(lp)) {
                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
                holder.itemView.setLayoutParams(rvLayoutParams);
            } else {
                rvLayoutParams = (LayoutParams) lp;
            }
            rvLayoutParams.mViewHolder = holder;
            rvLayoutParams.mPendingInvalidate = fromScrap && bound;
            return holder.itemView;
        }


關於ViewHolder

在RecyclerView裡面,view是有多重狀態的,各種狀態在ViewHolder裡面定義。看看下面的程式碼:

public static abstract class ViewHolder {
      public final View itemView;
      int mPosition = NO_POSITION;
      int mOldPosition = NO_POSITION;
      long mItemId = NO_ID;
      int mItemViewType = INVALID_TYPE;
      int mPreLayoutPosition = NO_POSITION;
 
      // The item that this holder is shadowing during an item change event/animation
      ViewHolder mShadowedHolder = null;
      // The item that is shadowing this holder during an item change event/animation
      ViewHolder mShadowingHolder = null;
 
      /**
       * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
       * are all valid.
       */
      static final int FLAG_BOUND = 1 << 0;
 
      /**
       * The data this ViewHolder's view reflects is stale and needs to be rebound
       * by the adapter. mPosition and mItemId are consistent.
       */
      static final int FLAG_UPDATE = 1 << 1;
 
      /**
       * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
       * are not to be trusted and may no longer match the item view type.
       * This ViewHolder must be fully rebound to different data.
       */
      static final int FLAG_INVALID = 1 << 2;
 
      /**
       * This ViewHolder points at data that represents an item previously removed from the
       * data set. Its view may still be used for things like outgoing animations.
       */
      static final int FLAG_REMOVED = 1 << 3;
 
      /**
       * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
       * and is intended to keep views around during animations.
       */
      static final int FLAG_NOT_RECYCLABLE = 1 << 4;
 
      /**
       * This ViewHolder is returned from scrap which means we are expecting an addView call
       * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
       * the end of the layout pass and then recycled by RecyclerView if it is not added back to
       * the RecyclerView.
       */
      static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
 
      /**
       * This ViewHolder's contents have changed. This flag is used as an indication that
       * change animations may be used, if supported by the ItemAnimator.
       */
      static final int FLAG_CHANGED = 1 << 6;
 
      /**
       * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
       * it unless LayoutManager is replaced.
       * It is still fully visible to the LayoutManager.
       */
      static final int FLAG_IGNORE = 1 << 7;

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


轉自:http://www.cnblogs.com/halzhang/p/4445145.html

相關文章