一直都知道ListView 有View回收再利用的机制,一直都是去判断getView函数中的 convertView是否为null从而确定是否要重新创建View, 一直都是这么用也没有多想原理是什么
今天开发过程中涉及到ListView中 显示多个种类的View,只是简单判断convertView是否为空肯定会出错,就算加上判断View是否是我们需要的种类,那么也还是会一定程度上降低效率(重新创建View的几率会很大)
想来想去一直这么迷迷糊糊下去也不是回事,决定去研究下源代码
ListView的父类是AbsListView,AbsListView 里面有个子类 RecycleBin,ListView中的 View就是在此处管理的
RecycleBin 类中的 三个变量涉及到此处的解析
private ArrayList<View>[] mScrapViews; 这个就是用来记录所有被回收的 View的地方
private int mViewTypeCount; 这个用于记录有多少种类的View
private ArrayList<View> mCurrentScrap; 这个很好理解 字面意思 当前的记录列表
其他的我们不看,比如这个View是怎么加进去之类的,我们就看看这个View是怎么获得 并传递到getView的 convertView的参数上的
其中 mViewTypeCount 的赋值操作:
public void setViewTypeCount(int viewTypeCount)
在ListView的setAdapter函数中有调用到这个函数
@Override
public void setAdapter(ListAdapter adapter) {
---省略代码---
if (mAdapter != null) {
---省略代码---
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
---省略代码---
}
由上面可以看出 viewTypeCount的值出自于 ListAdapter 也就是我们要在ListAdapter函数中实现getViewTypeCount()函数,返回我们的View种类
View种类的问题解决 我们看看这个种类是怎么用的,首先我们来看ListAdapter 的getView函数的调用的地方
View obtainView(int position, boolean[] isScrap) {
---省略代码---
scrapView = mRecycler.getScrapView(position);
View child;
if (scrapView != null) {
child = mAdapter.getView(position, scrapView, this);
---省略代码---
} else {
child = mAdapter.getView(position, null, this);
---省略代码---
}
---省略代码---
}
我们看到在调用getView的地方调用了RecycleBin类中getScrapView函数
/**
* @return A view from the ScrapViews collection. These are unordered.
*/
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;
}
我们看到 int whichScrap = mAdapter.getItemViewType(position);这句调用就是获取 View种类的地方
同样我们要在ListAdapter中实现getItemViewType(position) 这个函数,根据位置信息 我们判断类表相应位置的数据需要的View种类
那么我们现在知道了通过在ListAdapter函数中实现 getItemViewType(position) 和getViewTypeCount()函数,便能够实现不同种类的View的循环再利用了
另外通过这次解析ListView中有个监听View被回收的接口
public void setRecyclerListener(RecyclerListener listener)
public static interface RecyclerListener {
/**
* Indicates that the specified View was moved into the recycler's scrap heap.
* The view is not displayed on screen any more and any expensive resource
* associated with the view should be discarded.
*
* @param view
*/
void onMovedToScrapHeap(View view);
}
通过这个接口我们能知道那个view被回收了,我们可以在此处做一些释放内存的操作,比如View中包含了ImageView,那么我们就可以在此处把ImageView中使用的Bitmap干掉,如果达到了一定的数量级这个地方会非常有用