都知道ListView有复用View的机制,但是具体是怎么复用的却不怎么明白,里面的机制一概不知,百度了下也是云里雾里,果然纸上得来终觉浅,绝知此事要躬行,还是要自己深入源码去学习
RecycleBin的重要成员变量
private RecyclerListener mRecyclerListener;
//存储在mActiveViews中的第一个视图的位置。
private int mFirstActivePosition;
//可见的视图
private View[] mActiveViews = new View[0];
//适配器用来复用的废弃视图,也就是不可见的视图(缓存的)
//嵌套的列表,下标对应类型
private ArrayList<View>[] mScrapViews;
//View的种类
private int mViewTypeCount;
//ViewType == 1时mScrapViews第一个元素值
private ArrayList<View> mCurrentScrap;
private ArrayList<View> mSkippedScrap;
/*
TransientState是用来标记VIew的属性,对应下面两种类型
*/
//View对应的数据不变的TransientState的集合
private SparseArray<View> mTransientStateViews;
//View对应的控件Id不变的TransientState的View数组
private LongSparseArray<View> mTransientStateViewsById;
RecycleBin的方法
对于RecycleBin无非就是存和取,在最开始的时候初始化,在最后清空,所以找到对应的方法便可了解其机制。
里面的方法如下:
根据方法名也很容易知道他的功能是什么来。先看看有没有set方法
setCacheColorHint()应该不是我们要找到,那还剩下setViewTypeCount():
果然这里进行了初始化的工作
setViewTypeCount:
public void setViewTypeCount(int viewTypeCount) {
if (viewTypeCount < 1) {
throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
}
//根据设置的ViewType数目来初始化对应的ArrayList
ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];
//为每个ViewType创建一个存储不可见的View列表
for (int i = 0; i < viewTypeCount; i++) {
scrapViews[i] = new ArrayList<View>();
}
mViewTypeCount = viewTypeCount;
mCurrentScrap = scrapViews[0];
mScrapViews = scrapViews;
}
再来看看存的操作
有个addScrapView()这名字太明显了,就是添加不可见的视图,大概就是滑动的时候,部分View从可见变为不可见,把这些添加以便复用,来看看代码。。。。篇幅很长
addScrapView:
//添加视图到ScrapView列表中,如果该ListView的data没有改变或者适配器有固定ID(对应上面两个成员变量),
//则添加到对应transientstate集合中
void addScrapView(View scrap, int position) {
final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
if (lp == null) {
// Can't recycle, but we don't know anything about the view.
// Ignore it completely.
return;
}
//重新设置缓存ScrapView的位置,后面会用到
lp.scrappedFromPosition = position;
// Remove but don't scrap header or footer views, or views that
// should otherwise not be recycled.
final int viewType = lp.viewType;
//viewType > 0就会缓存
if (!shouldRecycleViewType(viewType)) {
// Can't recycle. If it's not a header or footer, which have
// special handling and should be ignored, then skip the scrap
// heap and we'll fully detach the view later.
//对不是顶部以及尾部的View进行缓存,应该很容易理解
if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
getSkippedScrap().add(scrap);
}
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);
// Don't scrap views that have transient state.
//不会去缓存含有transient state.标识的View
final boolean scrapHasTransientState = scrap.hasTransientState();
if (scrapHasTransientState) {
if (mAdapter != null && mAdapterHasStableIds) {
// If the adapter has stable IDs, we can reuse the view for
// the same data.
//如果该View具有固定的Id,可以复用数据
if (mTransientStateViewsById == null) {
mTransientStateViewsById = new LongSparseArray<>();
}
//存放进上面所说的TransientStateViews集合中
mTransientStateViewsById.put(lp.itemId, scrap);
} else if (!mDataChanged) {
// If the data hasn't changed, we can reuse the views at
// their old positions.
//如果数据未改变,可以重复使用该View在原位置处
if (mTransientStateViews == null) {
mTransientStateViews = new SparseArray<>();
}
mTransientStateViews.put(position, scrap);
} else {
// Otherwise, we'll have to remove the view and start over.
//其他情况放过过滤集合
clearScrapForRebind(scrap);
getSkippedScrap().add(scrap);
}
} else {
//下面就是不含有TransientState标识的View
clearScrapForRebind(scrap);
if (mViewTypeCount == 1) {
//只有一种类型就放人CurrentScrap列表中,与上面对应
mCurrentScrap.add(scrap);
} else {
mScrapViews[viewType].add(scrap);
}
//这个是监听回收状态的监听器
if (mRecyclerListener != null) {
mRecyclerListener.onMovedToScrapHeap(scrap);
}
}
}
fillActiveViews
//将absListView的所有子项存放到可见视图数组中
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();
// Don't put header or footer views into the scrap heap
//将不是顶部和尾部的child赋值到activeViews数组
if (lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
// Note: We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active views.
// However, we will NOT place them into scrap views.
activeViews[i] = child;
//纪录位置
lp.scrappedFromPosition = firstActivePosition + i;
}
}
}
看完了存的操作下面来看怎么取出来这些View
getActiveView
//获取与指定位置对应的视图。 如果找到,视图将从mActiveViews中删除。
View getActiveView(int position) {
//index就是当前位置-第一个可见视图位置
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;
}
getScrapView
//从缓存视图集合中获取指定位置的视图
View getScrapView(int position) {
//判断当前位置的视图类型ViewType
final int whichScrap = mAdapter.getItemViewType(position);
if (whichScrap < 0) {
return null;
}
//从ScrapView集合中获取
if (mViewTypeCount == 1) {
return retrieveFromScrap(mCurrentScrap, position);
} else if (whichScrap < mScrapViews.length) {
return retrieveFromScrap(mScrapViews[whichScrap], position);
}
return null;
}
getTransientStateView
//根据position获取TransientStateView,获取后会移除相应的View,和上面的类似
View getTransientStateView(int position) {
if (mAdapter != null && mAdapterHasStableIds && mTransientStateViewsById != null) {
long id = mAdapter.getItemId(position);
View result = mTransientStateViewsById.get(id);
mTransientStateViewsById.remove(id);
return result;
}
if (mTransientStateViews != null) {
final int index = mTransientStateViews.indexOfKey(position);
if (index >= 0) {
View result = mTransientStateViews.valueAt(index);
mTransientStateViews.removeAt(index);
return result;
}
}
return null;
}
//这个就是获取过滤数组,作用不大
private ArrayList<View> getSkippedScrap() {
if (mSkippedScrap == null) {
mSkippedScrap = new ArrayList<>();
}
return mSkippedScrap;
}
下面来看清除操作
//用于清空所有的缓存视图数组
void clear() {
//根据VIewType数目来清除
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();
}
再来看看clearScrap()方法,都很容易理解
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);
}
}
还有个
clearTransientStateViews
//清空保存的TransientState标识的集合
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();
}
}
fullyDetachScrapViews
//用来在ListView移除那些已经不可见的视图的关联,确保不可见视图列表中的任何剩余视图完全分离。
void fullyDetachScrapViews() {
final int viewTypeCount = mViewTypeCount;
final ArrayList<View>[] scrapViews = mScrapViews;
for (int i = 0; i < viewTypeCount; ++i) {
final ArrayList<View> scrapPile = scrapViews[i];
for (int j = scrapPile.size() - 1; j >= 0; j--) {
final View view = scrapPile.get(j);
if (view.isTemporarilyDetached()) {
removeDetachedView(view, false);
}
}
}
}
还有一些其他方法
markChildrenDirty
//用来重新调用缓存的child的forcelayout,forcelayout只会重新执行自己的onMeasure跟onLayout,不会调用父级的requestLayout()或forceLayout()。此方法会在ListView的size改变的时候进行调用。
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();
}
}
}
下面来看ListView使用缓存的流程,借用RecyclerView与ListView 对比浅析:缓存机制中的图片
参考:
深入理解Android中的缓存机制(二)RecyclerView跟ListView缓存机制对比
RecyclerView与ListView 对比浅析:缓存机制