您的位置:首页 > 其它

ListView原理分析之重要方法介绍

2015-09-17 17:27 337 查看
ListView直接继承自的AbsListView,而AbsListView有两个子实现类,一个是ListView,另一个就是GridView,

因此我们从这一点就可以猜出来,ListView和GridView在工作原理和实现上都是有很多共同点的。然后AbsListView

又继承自AdapterView,AdapterView继承自ViewGroup,后面就是我们所熟知的了。

Adapter

  说道ListView就会想到adapter,经常看到它们配套使用,为什么要使用adapter呢?

1、适配多种类型数据

控件就是为了交互和展示数据,ListView控件,它比较特殊,可以展示多种类型的数据交互和显示,比如array,list,Cursor,

或者Object,各种自定义类型,要实现多类型数据交互,要是都在View中处理,就不太灵活,adapter就充当了数据转换的桥梁。

2、重用机制RecycleBin

不管你有千条万条,ListView通过重用机制,减少了内存开销,及读xml的次数,可以说这才是ListView的精髓。



如上图所示:频幕中可显示6个activit的Item,向上滑动,Item1滑出屏幕,这时Item1被缓存起来,当Item7要出

现在屏幕时,就去缓存中,找到已经缓存好的view,刷新数据。这样不管你有多少条数据,也只是对这第一次

建立的6个Item View重用。

既然这么强大,我们就来分析下RecycleBin

比较重要的几个成员变量

mActiveViews  
       // View[ ]  存放当前可见View,也就是上图6个可见的Item

mCurrentScrap       // ArrayList<View> 存放废弃的View,也就是当Item1滑出屏幕后,就被添加到这个list中了

mScrapViews        //
ArrayList<View>[ ] 
存放废弃的Views,这个数组是在多类型布局中用到,与它有关的变

                                 
量ViewTypeCount,在adapter使用了getViewTypeCount() 后,会把View缓存到这个数组中

几个重要的方法:

fillActiveViews() 这个方法接收两个参数,第一个参数表示要存储的view的数量,第二个参数表示ListView中第一个可见元素的position值。RecycleBin当中使用mActiveViews这个数组来存储View,调用这个方法后就会根据传入的参数来将ListView中的指定元素存储到mActiveViews数组当中。

  /**
* Fill ActiveViews with all of the children of the AbsListView.
*
* @param childCount The minimum number of views mActiveViews should hold
* @param firstActivePosition The position of the first view that will be stored in
* mActiveViews
*/
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
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;
}
}
}

getActiveView() 这个方法和fillActiveViews()是对应的,用于从mActiveViews数组当中获取数据。该方法接收一个position参数,表示元素在ListView当中的位置,方法内部会自动将position值转换成mActiveViews数组对应的下标值。需要注意的是,mActiveViews当中所存储的View,一旦被获取了之后就会从mActiveViews当中移除,下次获取同样位置的View将会返回null,也就是说mActiveViews不能被重复利用。
View getActiveView(int position) {
int index = position - mFirstActivePosition;
final View[] activeViews = mActiveViews;
if (index >=0 && index < activeViews.length) {
final View match = activeViews[index];
<span style="color:#3333FF;"> activeViews[index] = null;</span>
return match;
}
return null;
}

addScrapView() 用于将一个废弃的View进行缓存,该方法接收一个View参数,当有某个View确定要废弃掉的时候(比如滚动出了屏幕),就应该调用这个方法来对View进行缓存,RecycleBin当中使用mScrapViews和mCurrentScrap这两个List来存储废弃View。
if (scrapHasTransientState) {
// 快速滑动过渡状态处理,这里不分析
  } else {
if (mViewTypeCount == 1) {
mCurrentScrap.add(scrap);
} else {
mScrapViews[viewType].add(scrap);
}

if (mRecyclerListener != null) {
mRecyclerListener.onMovedToScrapHeap(scrap);
}
}

getScrapView 用于从废弃缓存中取出一个View,这些废弃缓存中的View是没有顺序可言的,因此getScrapView()方法中的算法也非常简单,就是直接从mCurrentScrap当中获取尾部的一个scrap view进行返回。
        View getScrapView(int position) {
if (mViewTypeCount == 1) {
return retrieveFromScrap(mCurrentScrap, position);
} else {
final int whichScrap = mAdapter.getItemViewType(position);
if (whichScrap >= 0 && whichScrap < mScrapViews.length) {
return retrieveFromScrap(mScrapViews[whichScrap], position);
}
}
return null;
}

setViewTypeCount() 我们都知道Adapter当中可以重写一个getViewTypeCount()来表示ListView中有几种类型的数据项,而setViewTypeCount()方法的作用就是为每种类型的数据项都单独启用一个RecycleBin缓存机制。实际上,getViewTypeCount()方法通常情况下使用的并不是很多,所以我们只要知道RecycleBin当中有这样一个功能就行了。

    public void setViewTypeCount(int viewTypeCount) {
if (viewTypeCount < 1) {
throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
}
//noinspection unchecked
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;
}        
setViewTypeCount()赋值实在adapter中调用getViewTypeCount()后得到ViewTypeCount。

说道这,都了解了listView是怎么缓存的了吧,具体怎么绘制就不多说了,都是onMeasure()用于测量View的大小,onLayout()用于确定View的布局,

onDraw()用于将View绘制到界面上。onMeasure,onDraw没什么特别的,需要进一步分析的可以看看ListView的父类AbsListView中实现的onLayout
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: