0%

android-listview-分析

分析

从Adapter.getView方法入手, 找到了 AbsListView.obtainView,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  /**
* Get a view and have it show the data associated with the specified
* position. This is called when we have already discovered that the view is
* not available for reuse in the recycle bin. The only choices left are
* converting an old view or making a new one.
*
* @param position The position to display
* @param isScrap Array of at least 1 boolean, the first entry will become true if
* the returned view was taken from the scrap heap, false if otherwise.
*
* @return A view displaying the data associated with the specified position
*/
View obtainView(int position, boolean[] isScrap) {
isScrap[0] = false;
// Check whether we have a transient state view. Attempt to re-bind the
// data and discard the view if we fail.
final View transientView = mRecycler.getTransientStateView(position);
if (transientView != null) {
final LayoutParams params = (LayoutParams) transientView.getLayoutParams();
// If the view type hasn't changed, attempt to re-bind the data.
if (params.viewType == mAdapter.getItemViewType(position)) {
final View updatedView = mAdapter.getView(position, transientView, this);
// If we failed to re-bind the data, scrap the obtained view.
if (updatedView != transientView) {
setItemViewLayoutParams(updatedView, position);
mRecycler.addScrapView(updatedView, position);
}
}
isScrap[0] = true;
// Finish the temporary detach started in addScrapView().
transientView.dispatchFinishTemporaryDetach();
return transientView;
}
final View scrapView = mRecycler.getScrapView(position);
final View child = mAdapter.getView(position, scrapView, this);
if (scrapView != null) {
if (child != scrapView) {
// Failed to re-bind the data, return scrap to the heap.
mRecycler.addScrapView(scrapView, position);
} else {
isScrap[0] = true;
// Finish the temporary detach started in addScrapView().
child.dispatchFinishTemporaryDetach();
}
}
...... // 省略部分代码

return child;
}

我们把目光放在mRecycler上,其实就是一个RecyclerBin对象,负责缓存View对象,这是ListView复用机制的精华所在,有必要好好刨析这个类. RecyclerBin中各个变量的定义如下:

//回收Listener,当View变为可回收,即ScrapView时,会通过mRecyclerListener通知注册者,listener可通过setRecyclerListener注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private RecyclerListener mRecyclerListener;
/**
* The position of the first view stored in mActiveViews.
*/
// 第一个活动view的position,即第一个可视view的position
private int mFirstActivePosition;
/**
* Views that were on screen at the start of layout. This array is populated at the start of
* layout, and at the end of layout all view in mActiveViews are moved to mScrapViews.
* Views in mActiveViews represent a contiguous range of Views, with position of the first
* view store in mFirstActivePosition.
*/
// 活动view的集合
private View[] mActiveViews = new View[0];
/**
* Unsorted views that can be used by the adapter as a convert view.
*/
/**废弃的可修复view集合,复用时传递到Adapter#getView方法的convertView参数。
* 因为item type可能大于1,只有view type相同的view之间才能复用,所以是个二维数组
*/
private ArrayList<View>[] mScrapViews;
// ListView item type数量
private int mViewTypeCount;
// 当前的废弃view数组,定义这个成员是为了在mViewTypeCount为1时使用方便,不需要去取mScrapViews的第一个元素
private ArrayList<View> mCurrentScrap;
// 被跳过的,不能复用的view集合。view type小于0或者处理transient状态的view不能被复用。
private ArrayList<View> mSkippedScrap;
// 处于 transient状态的view集合,处于transient状态的view不能被复用,如view的动画正在播放,
// transient是瞬时、过渡的意思,关于transient状态详见android.view.View#PFLAG2_HAS_TRANSIENT_STATE
private SparseArray<View> mTransientStateViews;
// 如果adapter的hasStableIds方法返回true,处于过度状态的view保存到这里。因为需要保存view的position,而且处于过度状态的view一般很少,
// 这2个成员用了稀疏数组。具体不需要case,知道是保存转换状态view的集合就行。
private LongSparseArray<View> mTransientStateViewsById;

参考