继承关系
1、UML图
图中单独画出Scrollview是为了说明该ViewGroup并没有自带回收机制,如果要是Scrollview显示大量view,需要手动做处理。
2、继承体系的分工
(1) AdapterView
An AdapterView is a view whose children aredetermined by an {@link Adapter}.
a.adapter相关的抽象函数:getAdapter、setAdapter
b.mEmptyView
c.观察者模式
d. Accessibility:Android提供了Accessibility功能和服务帮助这些用户更加简单地操作设备,包括文字转语音,触觉反馈,手势操作,轨迹球和手柄操作。
(2)AbsListView
Base class that can be used to implementvirtualized lists of items. A list does not have a spatial definition here. Forinstance, subclases of this class can display
the content of the list in agrid, in a carousel, as stack, etc.
1.拥有RecycleBin类,负责处理view的生成和回收,没有子视图的空间定位信息
2.滑动事件相关(touch orfling-based scrolling)
(3)ListView
A view that shows items in a verticallyscrolling list. The items come from the {@link ListAdapter} associated withthis view.
以垂直列表显示数据,具体控制列表项的排布和位移
Header和footer相关
3.优点:层次清晰,易于扩展
RecycleBin类(实现回收机制)
1.成员变量
(1)mFirstActivePosition
The position of the first view stored inmActiveViews.
存储在mActiveViews中的第一个view的位置,即一个可视的子视图
(2)mActiveViews
View[]
Views that were on screen at the start oflayout. This array is populated at the start of layout, and at the end oflayout all view in mActiveViews are moved to
mScrapViews. Views in mActiveViewsrepresent a contiguous range of Views, with position of the first view store in
mFirstActivePosition.
布局开始时屏幕显示的view,这个数组会在布局开始时填充,布局结束后所有view被移至mScrapViews。
(3)mScrapViews
ArrayList<View>[]
Unsorted views that can be used by theadapter as a convert view.
可以被适配器用作convert view的无序view数组。这个ArrayList就是adapter中getView方法中的参数convertView的来源。注意:这里是一个数组,因为如果adapter中数据有多种类型,那么就会有多个ScrapViews。
2.成员函数
(1)shouldRecycleViewType
判断给定的view的viewType指明是否可以回收回。
viewType < 0可以回收。指定忽略的(ITEM_VIEW_TYPE_IGNORE
= -1),或者是 HeaderView / FootView(ITEM_VIEW_TYPE_HEADER_OR_FOOTER
= -2)是不被回收的。如有特殊需要可以将自己定义的viewType设置为-1,否则,将会浪费内存,导致OOM。
(2) retrieveFromScrap
a.第三种情况,这个最简单:
一开始,listview稳定后,此时mScrapView中是没有缓存view的,当我们向上滚动一小段距离(第一个此时仍显示部分),新的view将会显示,此时listview会调用Adapter.getView,但是缓存中没有,因此convertView是null,所以,我们得分配一块内存来创建新的convertView;
b.第二种情况:
在a中,我们继续向上滚动,直接第一个view完全移出屏幕(假设没有新的item),此时,第一个view就会被detach,并被加入到mScrapView中;然后,我们还继续向上滚动,直接后面又将要显示新的item
view时,此时,系统会从mScrapView中找position对应的View,显然,是找不到的,则将从mScrapView中,取最后一个缓存的view传递给convertView;
c.第一种情况:
AbsListView中的LayoutParams继承了ViewGroup的LayoutParams,其中一个增加的成员变量是scrappedFromPosition,这个变量是由addScrapView时传递进来的position赋值,就是当时要回收的view的位置。所以scrappedFromPosition==
position的判断就是,如果某个位置的view被回收了,此时该位置又要重新显示出来,那么优先显示原来的view,就是尽量保持原来位置的view不变。当然,如果原来的view已经用在别的位置了,那就没办法了。
(3) pruneScrapViews
确保mScrapViews的数目不会超过mActiveViews的数目
(This can happen if an adapter does not recycle its views)。mScrapView中每个ScrapView数组大小不应该超过mActiveView的大小,如果超过,系统认为程序并没有复用convertView,而是每次都是创建一个新的view,为了避免产生大量的闲置内存且增加OOM的风险,系统会在每次回收后,去检查一下,将超过的部分释放掉,节约内存降低OOM风险。
生成新子视图,即调用mAdapter.getView
1.private void scrollListItemsBy(int amount)
调用情景:按列表项逐项移动,由onKeyDown、onKeyUp、onKeyMultiple调用
commonKey-->arrowScrollImpl-->arrowScrollImpl-->scrollListItemsBy
(1)初始化:
其中offsetChildrenTopAndBottom是将所有子视图移动指定的像素:
最终调用mDisplayList的offsetTopBottom
(2)向上滑动的分支:
(3)
2.列表滑动生成列表项视图
(1)滑动时的调用堆栈
onTouchEvent (AbsListView)àscrollIfNeeded
(AbsListView)
或者FlingRunnable (AbsListView)
à
trackMotionScroll (AbsListView)
à
fillGap
(在AbsListView中为抽象函数,在ListView中实现;向下调用fillDown,向上调用fillUp)
(在调用fillGap之前会调用offsetChildrenTopAndBottom)
à
makeAndAddView (ListView)
没有数据变化:从mRecycler中取得可视的view
数据有变化:obtainView-->setupChild
(2)重点分析:
1)obtainView:生成view,回收旧view和调用mApapter.getView的地方(AbsListView)
2)第一行的getScrapView最后调用第二部分分析RecycleBin类的retrieveFromScrap
3)setupChild:将view添加到viewgroup,对子视图定位(ListView)
关键步骤:
a. attachViewToParent或者addViewInLayout
b. child.measure(childWidthSpec,childHeightSpec);
c. child.layout(childrenLeft, childTop,childRight, childBottom);
3.
再谈回收
如果实现了RecyclerListener接口,当一个View由于ListView的滑动被系统回收到RecycleBin的mScrapViews数组时,会调用RecyclerListener中的onMovedToScrapHeap(View
view)方法。RecycleBin相当于一个临时存储不需要显示的那部分Views的对象,随着列表滑动,这些Views需要显示出来的时候,他们就被从RecycleBin中拿了出来,RecycleBin本身并不对mScrapViews中的对象做回收操作。
于是在工程里,为ListView添加RecyclerListener接口,并在onMovedToScrapHeap方法中释放ListItem包含的Bitmap资源,这样可以极大的减少内存占用。
关于RecyclerListener接口的注释:
Sets the recycler listener to be notifiedwhenever a View is set aside in the recycler for later reuse. This listener canbe used to free resources associated to
the View.