RecycleBin可以说是AbsListView视图复用的核心工具类了。
其中最核心的成员变量应该就是mActivityViews和mScrapViews了。本文主要对RecycleBin中一些方法的功能做了简要的概述。有几句代码的功能其实我也还不太清楚,不过也不太影响我们对RecycleBin功能的理解了。
/**
* The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of
* storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the
* start of a layout. By construction, they are displaying current information. At the end of
* layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that
* could potentially be used by the adapter to avoid allocating views unnecessarily.
*
* @see android.widget.AbsListView#setRecyclerListener(android.widget.AbsListView.RecyclerListener)
* @see android.widget.AbsListView.RecyclerListener
*/
class RecycleBin {
// 当view被移动到scrap堆时的回调
private RecyclerListener mRecyclerListener;
// 存储在mActrivityViews中的第一个View(mActivityViews[0])在AbsListView中的position
private int mFirstActivePosition;
// ArrayList数组,缓存各种ViewType的视图,Adapter.getView方法参数中的convertView就是从这里来的
private View[] mActiveViews = new View[0];
// ArrayList数组,缓存各种ViewType的视图,Adapter.getView方法参数中的convertView就是从这里来的
private ArrayList<View>[] mScrapViews;
// 对应Adapter.getViewTypeCOunt()
private int mViewTypeCount;
private ArrayList<View> mCurrentScrap;
private ArrayList<View> mSkippedScrap;
private SparseArray<View> mTransientStateViews;
private LongSparseArray<View> mTransientStateViewsById;
// 初始化mViewTypeCount、mScrapViews,并设置mCurrentScrap为mScrapViews[0]
public void setViewTypeCount(int viewTypeCount) {
if (viewTypeCount < 1) {
throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
}
//noinspection unchecked
ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];
for (int i = 0; i < viewTypeCount; i++) {
scrapViews[i] = new ArrayList<View>();
}
mViewTypeCount = viewTypeCount;
mCurrentScrap = scrapViews[0];
mScrapViews = scrapViews;
}
// 调用保存在mScrapViews、mTransientStateViews、mTransientStateViewsById中view的forceLayout方法
public void markChildrenDirty() {
if (mViewTypeCount == 1) {
final ArrayList<View> scrap = mCurrentScrap;
final int scrapCount = scrap.size();
for (int i = 0; i < scrapCount; i++) {
scrap.get(i).forceLayout();
}
} else {
final int typeCount = mViewTypeCount;
for (int i = 0; i < typeCount; i++) {
final ArrayList<View> scrap = mScrapViews[i];
final int scrapCount = scrap.size();
for (int j = 0; j < scrapCount; j++) {
scrap.get(j).forceLayout();
}
}
}
if (mTransientStateViews != null) {
final int count = mTransientStateViews.size();
for (int i = 0; i < count; i++) {
mTransientStateViews.valueAt(i).forceLayout();
}
}
if (mTransientStateViewsById != null) {
final int count = mTransientStateViewsById.size();
for (int i = 0; i < count; i++) {
mTransientStateViewsById.valueAt(i).forceLayout();
}
}
}
public boolean shouldRecycleViewType(int viewType) {
return viewType >= 0;
}
// 清空mScrapViews、mTransientStateViews、mTransientStateViewsById从的视图,并调用ViewGroup.removeDetachedView移除
void clear() {
if (mViewTypeCount == 1) {
final ArrayList<View> scrap = mCurrentScrap;
clearScrap(scrap);
} else {
final int typeCount = mViewTypeCount;
for (int i = 0; i < typeCount; i++) {
final ArrayList<View> scrap = mScrapViews[i];
clearScrap(scrap);
}
}
clearTransientStateViews();
}
// 填充mActivityViews,视图要求:LayoutParams不得为null,不得为header或者footer
void fillActiveViews(int childCount, int firstActivePosition) {
if (mActiveViews.length < childCount) {
mActiveViews = new View[childCount];
}
mFirstActivePosition = firstActivePosition;
//noinspection MismatchedReadAndWriteOfArray
final View[] activeViews = mActiveViews;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams();
// 不为header或footer,并且LayoutParams不为null才会填充
if (lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
activeViews[i] = child;
}
}
}
// 从mActiveViews获取指定position的View,并从mActiveViews中移除该View
View getActiveView(int position) {
int index = position - mFirstActivePosition;
final View[] activeViews = mActiveViews;
if (index >=0 && index < activeViews.length) {
final View match = activeViews[index];
activeViews[index] = null;
return match;
}
return null;
}
View getTransientStateView(int position) {
// mAdapterHasStableIds的值为mAdapter.hasStableIds(),在setAdapter时会被设置
if (mAdapter != null && mAdapterHasStableIds && mTransientStateViewsById != null) {
// 从mTransientStateViewsById中获取
long id = mAdapter.getItemId(position);
View result = mTransientStateViewsById.get(id);
mTransientStateViewsById.remove(id);
return result;
}
if (mTransientStateViews != null) {
// 从mTransientStateViews中获取
final int index = mTransientStateViews.indexOfKey(position);
if (index >= 0) {
View result = mTransientStateViews.valueAt(index);
mTransientStateViews.removeAt(index);
return result;
}
}
return null;
}
/**
* 清空mTransientStateViews和mTransientStateViewsById
*/
void clearTransientStateViews() {
final SparseArray<View> viewsByPos = mTransientStateViews;
if (viewsByPos != null) {
final int N = viewsByPos.size();
for (int i = 0; i < N; i++) {
removeDetachedView(viewsByPos.valueAt(i), false);
}
viewsByPos.clear();
}
final LongSparseArray<View> viewsById = mTransientStateViewsById;
if (viewsById != null) {
final int N = viewsById.size();
for (int i = 0; i < N; i++) {
removeDetachedView(viewsById.valueAt(i), false);
}
viewsById.clear();
}
}
/**
* 从缓存中获取一个备用视图
*/
View getScrapView(int position) {
if (mViewTypeCount == 1) {
// viewtype为1,那么直接从mCurrentScrap中获取
return retrieveFromScrap(mCurrentScrap, position);
} else {
// viewtype不为1,获取position对应的type,然后以此为下标,从mScrapViews中获取
final int whichScrap = mAdapter.getItemViewType(position);
if (whichScrap >= 0 && whichScrap < mScrapViews.length) {
return retrieveFromScrap(mScrapViews[whichScrap], position);
}
}
return null;
}
/**
* Puts a view into the list of scrap views.
* 向缓存中放入一个视图
* @param scrap The view to add
* @param position The view's position within its parent
* 该视图在其父布局中的position
*/
void addScrapView(View scrap, int position) {
final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
// 不添加LayoutParams为null的视图
if (lp == null) {
return;
}
// 记录位置
lp.scrappedFromPosition = position;
// header或footer的viewType为-2
final int viewType = lp.viewType;
if (!shouldRecycleViewType(viewType)) {
// 如果viewType小于0那么直接return,不进行回收
return;
}
scrap.dispatchStartTemporaryDetach();
// The the accessibility state of the view may change while temporary
// detached and we do not allow detached views to fire accessibility
// events. So we are announcing that the subtree changed giving a chance
// to clients holding on to a view in this subtree to refresh it.
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
// 如果有Transient State那么不放到mScrapViews中
final boolean scrapHasTransientState = scrap.hasTransientState();
if (scrapHasTransientState) {
if (mAdapter != null && mAdapterHasStableIds) {
// 放到mTransientStateViewsById中
if (mTransientStateViewsById == null) {
mTransientStateViewsById = new LongSparseArray<View>();
}
mTransientStateViewsById.put(lp.itemId, scrap);
} else if (!mDataChanged) {
// 放到mTransientStateViews中
if (mTransientStateViews == null) {
mTransientStateViews = new SparseArray<View>();
}
mTransientStateViews.put(position, scrap);
} else {
// 放到mSkippedScrap中
if (mSkippedScrap == null) {
mSkippedScrap = new ArrayList<View>();
}
mSkippedScrap.add(scrap);
}
} else {
// 放到mScrapViews中
if (mViewTypeCount == 1) {
mCurrentScrap.add(scrap);
} else {
mScrapViews[viewType].add(scrap);
}
// 回调
if (mRecyclerListener != null) {
mRecyclerListener.onMovedToScrapHeap(scrap);
}
}
}
/**
* 清空mSkippedScrap
*/
void removeSkippedScrap() {
if (mSkippedScrap == null) {
return;
}
final int count = mSkippedScrap.size();
for (int i = 0; i < count; i++) {
removeDetachedView(mSkippedScrap.get(i), false);
}
mSkippedScrap.clear();
}
/**
* 转移mActiveViews中的view到mScrapViews中去,一般是在layout尾声的时候被调用
*/
void scrapActiveViews() {
final View[] activeViews = mActiveViews;
final boolean hasListener = mRecyclerListener != null;
final boolean multipleScraps = mViewTypeCount > 1;
ArrayList<View> scrapViews = mCurrentScrap;
final int count = activeViews.length;
for (int i = count - 1; i >= 0; i--) {
final View victim = activeViews[i];
if (victim != null) {
final AbsListView.LayoutParams lp
= (AbsListView.LayoutParams) victim.getLayoutParams();
final int whichScrap = lp.viewType;
// 从mActiveViews中移除
activeViews[i] = null;
if (victim.hasTransientState()) {
// 视图拥有Transient State
// Store views with transient state for later use.
victim.dispatchStartTemporaryDetach();
if (mAdapter != null && mAdapterHasStableIds) {
// 放到mTransientStateViewsById中
if (mTransientStateViewsById == null) {
mTransientStateViewsById = new LongSparseArray<View>();
}
long id = mAdapter.getItemId(mFirstActivePosition + i);
mTransientStateViewsById.put(id, victim);
} else if (!mDataChanged) {
// 放到mTransientStateViews中
if (mTransientStateViews == null) {
mTransientStateViews = new SparseArray<View>();
}
mTransientStateViews.put(mFirstActivePosition + i, victim);
} else if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
// 数据源都改变了,留它无用。。
removeDetachedView(victim, false);
}
} else if (!shouldRecycleViewType(whichScrap)) {
// 不允许回收
// 移除所有非header、footer的视图
if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
removeDetachedView(victim, false);
}
} else {
// 转移到mScrapViews中
if (multipleScraps) {
scrapViews = mScrapViews[whichScrap];
}
victim.dispatchStartTemporaryDetach();
lp.scrappedFromPosition = mFirstActivePosition + i;
scrapViews.add(victim);
// 回调
if (hasListener) {
mRecyclerListener.onMovedToScrapHeap(victim);
}
}
}
}
// 别急,马上就讲
pruneScrapViews();
}
/**
* Makes sure that the size of mScrapViews does not exceed the size of
* mActiveViews, which can happen if an adapter does not recycle its
* views. Removes cached transient state views that no longer have
* transient state.
* 确保mScrapViews的大小不会超过mActiveViews。这种情况有可能发生在adapter没有回收view
* 删除不再有transient state的view
*/
private void pruneScrapViews() {
final int maxViews = mActiveViews.length;
final int viewTypeCount = mViewTypeCount;
final ArrayList<View>[] scrapViews = mScrapViews;
for (int i = 0; i < viewTypeCount; ++i) {
final ArrayList<View> scrapPile = scrapViews[i];
int size = scrapPile.size();
final int extras = size - maxViews;
size--;
for (int j = 0; j < extras; j++) {
removeDetachedView(scrapPile.remove(size--), false);
}
}
final SparseArray<View> transViewsByPos = mTransientStateViews;
if (transViewsByPos != null) {
for (int i = 0; i < transViewsByPos.size(); i++) {
final View v = transViewsByPos.valueAt(i);
if (!v.hasTransientState()) {
removeDetachedView(v, false);
transViewsByPos.removeAt(i);
i--;
}
}
}
final LongSparseArray<View> transViewsById = mTransientStateViewsById;
if (transViewsById != null) {
for (int i = 0; i < transViewsById.size(); i++) {
final View v = transViewsById.valueAt(i);
if (!v.hasTransientState()) {
removeDetachedView(v, false);
transViewsById.removeAt(i);
i--;
}
}
}
}
/**
* 将mScrapViews中所有的视图放到所提供的List中
*/
void reclaimScrapViews(List<View> views) {
if (mViewTypeCount == 1) {
views.addAll(mCurrentScrap);
} else {
final int viewTypeCount = mViewTypeCount;
final ArrayList<View>[] scrapViews = mScrapViews;
for (int i = 0; i < viewTypeCount; ++i) {
final ArrayList<View> scrapPile = scrapViews[i];
views.addAll(scrapPile);
}
}
}
/**
* Updates the cache color hint of all known views.
* 更新mScrapViews、mActiveViews中所有视图的DrawingCache
*/
void setCacheColorHint(int color) {
if (mViewTypeCount == 1) {
final ArrayList<View> scrap = mCurrentScrap;
final int scrapCount = scrap.size();
for (int i = 0; i < scrapCount; i++) {
scrap.get(i).setDrawingCacheBackgroundColor(color);
}
} else {
final int typeCount = mViewTypeCount;
for (int i = 0; i < typeCount; i++) {
final ArrayList<View> scrap = mScrapViews[i];
final int scrapCount = scrap.size();
for (int j = 0; j < scrapCount; j++) {
scrap.get(j).setDrawingCacheBackgroundColor(color);
}
}
}
// Just in case this is called during a layout pass
final View[] activeViews = mActiveViews;
final int count = activeViews.length;
for (int i = 0; i < count; ++i) {
final View victim = activeViews[i];
if (victim != null) {
victim.setDrawingCacheBackgroundColor(color);
}
}
}
// 从指定的scrapViews中获取指定position的视图
private View retrieveFromScrap(ArrayList<View> scrapViews, int position) {
final int size = scrapViews.size();
if (size > 0) {
// See if we still have a view for this position or ID.
for (int i = 0; i < size; i++) {
final View view = scrapViews.get(i);
final AbsListView.LayoutParams params =
(AbsListView.LayoutParams) view.getLayoutParams();
if (mAdapterHasStableIds) {
final long id = mAdapter.getItemId(position);
if (id == params.itemId) {
return scrapViews.remove(i);
}
} else if (params.scrappedFromPosition == position) {
final View scrap = scrapViews.remove(i);
clearAccessibilityFromScrap(scrap);
return scrap;
}
}
final View scrap = scrapViews.remove(size - 1);
clearAccessibilityFromScrap(scrap);
return scrap;
} else {
return null;
}
}
private void clearScrap(final ArrayList<View> scrap) {
final int scrapCount = scrap.size();
for (int j = 0; j < scrapCount; j++) {
removeDetachedView(scrap.remove(scrapCount - 1 - j), false);
}
}
private void clearAccessibilityFromScrap(View view) {
if (view.isAccessibilityFocused()) {
view.clearAccessibilityFocus();
}
view.setAccessibilityDelegate(null);
}
private void removeDetachedView(View child, boolean animate) {
child.setAccessibilityDelegate(null);
AbsListView.this.removeDetachedView(child, animate);
}
}