1.Listview
Listview是用来显示大量数据的控件,且不会因为展示大量数据而出现内存溢出的现象,原因是它的缓存机制保证了内存的合理使用。
首先看一下ListView的继承结构:
ListView直接继承自AbsListView,而AbsListView有两个子实现类,一个是ListView,一个是GridView,从这一点就可以看出来,ListView和GridView在工作原理和实现上都是有很多共同点的。然后AbsListView继承自AdapterView,AdapterView继承自ViewGroup。
2.RecycleBin机制
RecycleBin是ListView缓存的核心机制,它是ListView能够实现成百上千条数据都不会OOM最重要的原因。
RecycleBin是AbsListView的一个内部类,所有继承自AbsListView的子类,也就是ListView和GridView,都可以使用这个机制。
class RecycleBin {
private RecyclerListener mRecyclerListener;
private int mFirstActivePosition;
//mActiveViews存放正在显示在屏幕上的view ,从显示在屏幕上的第一个view到最后一个view
private View[] mActiveViews = new View[0];
//mScrapViews存放可以由适配器用作convert view的view,是一个数组,数组的每个元素类型为ArrayList<View>
private ArrayList<View>[] mScrapViews;
private int mViewTypeCount;
//mCurrentScrap是mScrapViews的第0个元素,当view种类数量为1时存放废弃view
private ArrayList<View> mCurrentScrap;
//Adapter中可以重写getViewTypeCount方法来表示ListView中有几种类型的数据项,而setViewTypeCount()方法就是为每种类型的数据项都单独启用一个RecycleBin缓存机制
public void setViewTypeCount(int viewTypeCount) {
if (viewTypeCount < 1) {
throw new IllegalArgumentException( "Can't have a viewTypeCount < 1");
}
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;
}
// fillActiveViews()接收两个参数,第一个参数表示mActiveViews数组最小要保存的View数量,第二个参数表示ListView中第一个可见元素的position值。 根据传入的参数将ListView中的指定元素存储到mActiveViews数组当中
void fillActiveViews(int childCount, int firstActivePosition) {
if (mActiveViews.length < childCount) {
mActiveViews = new View[childCount];
}
mFirstActivePosition = firstActivePosition;
final View[] activeViews = mActiveViews;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams();
if (lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
activeViews[i] = child;
lp.scrappedFromPosition = firstActivePosition + i;
}
}
}
//getActiveView()方法用于从mActiveViews数组中取出特定元素。该方法接收一个position参数,表示元素在ListView当中的位置,方法内部会自动将position值转换成mActiveViews数组对应的下标值。mActiveViews当中所存储的View,一旦被获取之后就会从mActiveViews当中移除,下次获取同样位置的View将会返回null,也就是说mActiveViews不能被重复利用。如果在mActiveViews数组中没有找到,则返回null
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;
}
//getScrapView()用于从废弃缓存中取出一个View。这些废弃缓存中的View是没有顺序可言的,因此getScrapView()方法中的算法非常简单,就是直接从mCurrentScrap当中获取尾部的一个scrap view进行返回
View getScrapView(int position) {
final int whichScrap = mAdapter.getItemViewType(position);
if (whichScrap < 0) {
return null;
}
if (mViewTypeCount == 1) {
return retrieveFromScrap(mCurrentScrap, position);
} else if (whichScrap < mScrapViews.length){
return retrieveFromScrap( mScrapViews[whichScrap], position);
}
return null;
}
private View retrieveFromScrap( ArrayList<View> scrapViews, int position) {
final int size = scrapViews.size();
if(size > 0) {
for(int i = size - 1; i >= 0; 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.scrapedFromPosition == position) {
final View scrap = scrapViews.remove(i);
clearScrapForRebind(scrap);
return scrap;
}
}
final View scrap = scrapViews.remove( size - 1);
clearScrapForRebind(scrap);
return scrap;
}
return null;
}
//addScrapView()用于将一个废弃的View进行缓存。该方法接收一个View参数,当有某个View确定要废弃掉的时候(比如滚动出了屏幕),应该调用这个方法来对View进行缓存。当view类型为1时则用mCurrentScrap存储废弃view,否则使用mScrapViews添加废弃view
void addScrapView(View scrap, int position) {
final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
if (lp == null) {
return;
}
lp.scrappedFromPosition = position;
final int viewType = lp.viewType;
if (!shouldRecycleViewType(viewType)) {
if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
getSkippedScrap().add(scrap);
}
return;
}
if (scrapHasTransientState) {
……
} else {
if (mViewTypeCount == 1) {
mCurrentScrap.add(scrap);
} else {
mScrapViews[viewType].add(scrap);
}
if (mRecyclerListener !=