下面简要分析android中BaseAdapter的getView()方法中convertView的来源(API19):
ListView一定在用户卷动列表的时候调用了adapter的getView()方法来获取要显示的新的项目的view对象。
然而ListView类的源代码中没有这个调用,所以应当是在其父类AbsListView中调用过,
我们在AbsListView类中查找adapter的getView()方法的调用作为追踪的起点:
1 AbsListView的obtainView()方法
//该方法调用了adapter的getView()方法
View obtainView(int position, boolean[] isScrap) {
........................
//scrapView是从mRecycler中取出的View对象
View scrapView;
scrapView =mRecycler.getTransientStateView(position);
if (scrapView == null) {
//从recycler中取出scrapView对象用于复用
scrapView = mRecycler.getScrapView(position);
}
View child;
//如果recycler中有可用的scrapView,就将scrapView传给adapter的getView()方法去复用
if (scrapView != null) {
//调用adapter的getView()方法,注意,scrapView就是getView()方法传入的形参convertView
child =mAdapter.getView(position, scrapView, this);
.........................
//如果adapter的getView()方法返回的View不是刚才传入的scrapView
//说明用户没有复用scrapView,它被重新放回到mRecylcler中
if (child != scrapView) {
mRecycler.addScrapView(scrapView, position);
...........................
}
}
//如果recycler中没有任何view可以拿出来传给adapter的getView()方法去复用,那就传null
else {
child = mAdapter.getView(position, null, this);
.......................
}
return child;
}
上面的源代码明确表示,用于复用的scrapView是调用mRecycler的getScrapView()方法得到的
而且不用的scrapView是调用mRecycler的addScrapView()方法放回mRecycler的
所以我们来看RecycleBin类和它的getScrapView()方法:
2 RecycleBin和它的getScrapView()方法
//上文中的mRecycler是RecycleBin类的一个对象
final RecycleBin mRecycler = new RecycleBin();
class RecycleBin{
..................
/** 原版注释:
* Views that were on screen at thestart of layout. This array is populated at the start of
* layout, and at the end of layout allview in mActiveViews are moved to mScrapViews.
* Views in mActiveViews represent acontiguous range of Views, with position of the first
* view store in mFirstActivePosition.
*/
/**原版注释的翻译:
* mActiveViews:
*布局生成伊始就显示在屏幕上的Views.这个数组在布局生成时就初始化,
* 最终所有在mActiveViews数组中的View都会被移动到mScrapViews。
* /
private View[]mActiveViews = new View[0];
/** 原版注释:
* Unsorted views that can be used by the adapter as a convert view.
*/
/** 原版注释的翻译:
* mScrapViews:
* 未排序的views,这些view可以被adapter用作复用
*/
privateArrayList<View>[] mScrapViews;
.....................................
/** 原版注释:
* @return A view from the ScrapViews collection. These are unordered.
*/
/** 原版注释的翻译:
* 从ScarpViews集合中返回一个View。返回的View是无序的。
*/
View getScrapView(int position) {
if (mViewTypeCount == 1) {
return retrieveFromScrap(mCurrentScrap, position);
} else {
int whichScrap =mAdapter.getItemViewType(position);
if (whichScrap >= 0&& whichScrap < mScrapViews.length) {
return retrieveFromScrap(mScrapViews[whichScrap],position);
}
}
return null;
}
..............................
}
看到这,我们发现RecycleBin类的getScrapView()方法实际上调用的是retrieveFromScrap()方法来获得ScrapView
而这个方法不是定义在RecycleBin类中的,是定义在AbsListView类中的一个静态方法:
3 AbsListView的retrieveFromScrap()方法
//RecycleBin的getScrapView()方法将RecycleBin类中的存储scrapViews的ArrayList传给下面的方法
//retrieveFromScrap()方法返回一个View给getScrapView()方法,进而传给adapter的getView()方法作为复用的view
static View retrieveFromScrap(ArrayList<View> scrapViews, intposition) {
int size = scrapViews.size();
//如果scrapViewarrayList中存储有scrapView
if (size > 0) {
//遍历arrayList中的所有scrapView
//去掉对应同一position的重复的scrapView
//如果刚好请求复用的position与scrapViewarrayList中某个scrapView的position相同
//则返回这个scrapView
for (int i=0; i<size; i++) {
View view = scrapViews.get(i);
if (((AbsListView.LayoutParams)view.getLayoutParams())
.scrappedFromPosition== position) {
scrapViews.remove(i);
return view;
}
}
//不巧没有对应同一position的scrapView,就将scrapViewarrayList的最后一项返回
-return scrapViews.remove(size 1);
//如果scrapViewarrayList中没有任何scrapView,返回null
} else {
return null;
}
}
4 总结
ListView中的RecycleBin内部类保存了一个View[]数组mActiveViews,用来保存屏幕上正在显示的所有View
还保存了一个ArrayList<View>集合mScrapViews,用来保存所有滑出屏幕等待复用的View。
当用户滑动屏幕时,ListView会从mActiveViews中将滑出屏幕的view加入mScrapViews
并从后者取出view传给adapter的getView()方法供其复用