几个概念
- RecyclerView是一个ViewGroup;
- LayoutManager控制RecyclerView的ChildView的布局显示,childview由Recycler提供以及管理;
- Recycler具有两级缓存,Scrap和RecycledViewPool,通过Detach以及Remove,对Viewholder进行转移以及状态改变;
- RecycledViewPool可以由多个RecyclerView共享;
- 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----------