1479-1729
1583-当数据发生改变的时候 把当前的view放到scrapviews里面 否则标记为activeViews。
// Pull all children into the RecycleBin.
// These views will be reused if possible
final int firstPosition mFirstPosition;
final RecycleBin recycleBin mRecycler;
if (dataChanged) {
for (int i i childCount; i ) {
recycleBin.addScrapView(getChildAt(i), firstPosition
}
}else {
recycleBin.fillActiveViews(childCount, firstPosition);
}
// Clear out old views
detachAllViewsFromParent();
recycleBin.removeSkippedScrap();//移除所有old views
......
// Flush any cached views that did not get reused above
recycleBin.scrapActiveViews();//刷新缓存 将当前的ActiveVies 移动到 ScrapViews。
dataChanged 从单词的意思我们就可以 这里的优化规则就是基于数据是否有变化 mDataChanged在makeAndAddView 见下文 中有使用。
step1 如果数据发生变化 就将所有view加入到mScrapView中 否则 将所有view放到mActiveView中
step2 添加view到listview中
step3 回收mActiveView中的未使用的view到mScrapView中
注 在step1中 如果是addScrapView 则所有的view将会detach 如果是fillActiveViews 则不会detach 只有在step3中 未用到的view才会detach。
3.1.2 makeAndAddView
(int position, int y, boolean flow, int childrenLeft, boolean selected)(1772)
Obtain the view and add it to our list of children. The view can be made fresh, converted from an unused view, or used as is if it was in the recycle bin.
View child;
if (!mDataChanged) {// 数据没有更新时 使用以前的view
// Try to use an existing view for this position
child mRecycler.getActiveView(position);
if (child ! null) {
// Found it -- we re using an existing child
// This just needs to be positioned
// 对复用的View针对当前需要进行配置。定位并且添加这个view到ViewGrop中的children列表 从回收站获取的视图不需要measure 所以最后一个参数为true
setupChild(child, position, y, flow, childrenLeft, selected, true);
return child;
}
}
// Make a new view for this position, or convert an unused view if possible
// 创建或者重用视图
child obtainView(position, mIsScrap);
// This needs to be positioned and measured
setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
return child;
3.1.3 setupChild
(View child, int position, int y, boolean flowDown, int childrenLeft, boolean selected, boolean recycled) 1812
Add a view as a child and make sure it is measured (if necessary) and positioned properly.
3.1.4 setAdapter
(ListAdapter adapter) (457)
Sets the data behind this ListView. The adapter passed to this method may be wrapped by a WrapperListAdapter, depending on the ListView features currently in use. For instance, adding headers and/or footers will cause the adapter to be wrapped.
if (mAdapter ! null mDataSetObserver ! null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);//移除了与当前listview的adapter绑定数据集观察者DataSetObserver
}
resetList();//重置listview 主要是清除所有的view 改变header、footer的状态
mRecycler.clear();//清除掉RecycleBin对象mRecycler中所有缓存的view RecycleBin后面着重介绍 主要是关系到Listview中item的重用机制 它是AbsListview的一个内部类
if (mHeaderViewInfos.size() 0|| mFooterViewInfos.size() 0) { //判断是否有headerview和footview
mAdapter new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter adapter;
}
mOldSelectedPosition INVALID_POSITION;
mOldSelectedRowId INVALID_ROW_ID;
// AbsListView#setAdapter will update choice mode states.
super.setAdapter(adapter);
if (mAdapter ! null) {
mAreAllItemsSelectable mAdapter.areAllItemsEnabled();
mOldItemCount mItemCount;
mItemCount mAdapter.getCount();
checkFocus();
mDataSetObserver new AdapterDataSetObserver();//注册headerview的观察者
mAdapter.registerDataSetObserver(mDataSetObserver);//在RecycleBin对象mRecycler记录下item类型的数量
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
int position;
if (mStackFromBottom) {
position lookForSelectablePosition(mItemCount - 1, false);
} else {
position lookForSelectablePosition(0, true);
}
setSelectedPositionInt(position);//AdapterView中的方法 记录当前的position
setNextSelectedPositionInt(position);//AdapterView中的方法 记录下一个position
if (mItemCount 0) {
// Nothing selected
checkSelectionChanged();
}
} else {
mAreAllItemsSelectable true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}
requestLayout();
3.1.5 scrollListItemsBy
(int amount) 3012-3082
对子view滑动一定距离 添加view到底部或者移除顶部的不可见view。从注释看 不可见的item 的自动移除是在scrollListItemsBy中进行的。
private void scrollListItemsBy(int amount) {
offsetChildrenTopAndBottom(amount);
final int listBottom getHeight() - mListPadding.bottom;//获取listview最底部位置
final int listTop mListPadding.top; //获取listview最顶部位置
final AbsListView.RecycleBin recycleBin mRecycler;
if (amount 0) {
// shifted items up
// may need to pan views into the bottom space
int numChildren getChildCount();
View last getChildAt(numChildren - 1);
while (last.getBottom() listBottom) {//最后的view高于底部时添加下一个view
final int lastVisiblePosition mFirstPosition numChildren - 1;
if (lastVisiblePosition mItemCount - 1) {
last addViewBelow(last, lastVisiblePosition);
numChildren
} else {
break;
}
}
// may have brought in the last child of the list that is skinnier
// than the fading edge, thereby leaving space at the end. need
// to shift back
if (last.getBottom() listBottom) {//到达最后一个view
offsetChildrenTopAndBottom(listBottom - last.getBottom());
}
// top views may be panned off screen
View first getChildAt(0);
while (first.getBottom() listTop) {//顶部view移除屏幕时
AbsListView.LayoutParams layoutParams (LayoutParams) first.getLayoutParams();
if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) {
recycleBin.addScrapView(first, mFirstPosition); //回收view
}
detachViewFromParent(first); //从父类中移除
first getChildAt(0); //这行好像没用啊。。。。
mFirstPosition
}
} else {
// shifted items down
View first getChildAt(0);
// may need to pan views into top
while ((first.getTop() listTop) (mFirstPosition 0)) {//顶部view上部有空间时添加view。
first addViewAbove(first, mFirstPosition);
mFirstPosition--;
}
// may have brought the very first child of the list in too far and
// need to shift it back
if (first.getTop() listTop) {//到达第一个view
offsetChildrenTopAndBottom(listTop - first.getTop());
}
int lastIndex getChildCount() - 1;
View last getChildAt(lastIndex);
// bottom view may be panned off screen
while (last.getTop() listBottom) {//底部view移除屏幕的情况
AbsListView.LayoutParams layoutParams (LayoutParams) last.getLayoutParams();
if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) {
recycleBin.addScrapView(last, mFirstPosition lastIndex);
}
detachViewFromParent(last);
last getChildAt(--lastIndex);
}
}
}
从以上代码可以看出 Android中view回收的计算是其父view中不再显示的 如果scrollview中包含了一个wrap_content属性的listview 里面的内容并不会有任何回收 引起listview 的getheight函数获取的是一个足以显示所有内容的高度。
3.2 AbsListView 3.2.1 obtainView(int position, boolean[] isScrap)(2227)
Get a view and have it show the data associated with the specified position. 当这个方法被调用时 说明Recycle bin中的view已经不可用了 那么 现在唯一的方法就是 convert一个老的view 或者构造一个新的view。
position: 要显示的位置
isScrap: 是个boolean数组, 如果view从scrap heap获取 isScrap [0]为true,否则为false。
isScrap[0] false;
View scrapView;
scrapView mRecycler.getTransientStateView(position);
if (scrapView null) {
// 查看回收站中是否有废弃无用的View 如果有 则使用它 无需New View。
scrapView mRecycler.getScrapView(position);
}
View child;
if (scrapView ! null) { //此时说明可以从回收站中重新使用scrapView。
child mAdapter.getView(position, scrapView, this);
if(child.getImportantForAccessibility() IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
if (child ! scrapView) {
//如果重用的scrapView和adapter获得的view是不一样的 将scrapView进行回收
mRecycler.addScrapView(scrapView, position);// scrapView 仍然放入回收站
if (mCacheColorHint ! 0) {
child.setDrawingCacheBackgroundColor(mCacheColorHint);
}
} else {
//如果重用的view和adapter获得的view是一样的 将isScrap[0]值为true 否则默认为false
isScrap[0] true;
// Clear any system-managed transient state so that we can
// recycle this view and bind it to different data.
if (child.isAccessibilityFocused()) {
child.clearAccessibilityFocus();
}
child.dispatchFinishTemporaryDetach();
}
}else {//回收站中没有拿到数据 就只能够自己去inflate一个xml布局文件 或者new一个view
child mAdapter.getView(position, null, this); //当getview中传入的
converView null的时候会在getView的方法中进行新建这个view
if (child.getImportantForAccessibility() IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
if (mCacheColorHint ! 0) {
child.setDrawingCacheBackgroundColor(mCacheColorHint);
}
}
3.2.2 trackMotionScroll
(int deltaY, int incrementalDeltaY)(4991)
监视滑动动作
deltaY: Amount to offset mMotionView. This is the accumulated delta since the motion began. 正数表示向下滑动。
incrementalDeltaY :Change in deltaY from the previous event.
.......
// 滚动时 不在可见范围内的item放入回收站。。。。。。。
if (down) {
int top -incrementalDeltaY;
if ((mGroupFlags CLIP_TO_PADDING_MASK) CLIP_TO_PADDING_MASK) {
top listPadding.top;
}
for (int i i childCount; i ) {
final View child getChildAt(i);
if (child.getBottom() top) {
break;
} else {
count
int position firstPosition
if (position headerViewsCount position footerViewsStart) {
// The view will be rebound to new data, clear any
// system-managed transient state.
if (child.isAccessibilityFocused()) {
child.clearAccessibilityFocus();
}
mRecycler.addScrapView(child, position);//放入回收站
}
}
}
} else {
int bottom getHeight() - incrementalDeltaY;
if ((mGroupFlags CLIP_TO_PADDING_MASK) CLIP_TO_PADDING_MASK) {
bottom - listPadding.bottom;
}
for (int i childCount - 1; i i--) {
final View child getChildAt(i);
if (child.getTop() bottom) {
break;
} else {
start
count
int position firstPosition
if (position headerViewsCount position footerViewsStart) {
// The view will be rebound to new data, clear any
// system-managed transient state.
if (child.isAccessibilityFocused()) {
child.clearAccessibilityFocus();
}
mRecycler.addScrapView(child, position);//放入回收站
}
}
}
}
4. 用到的关键类 4.1 ViewType的使用
在listview中当有多种viewtype的时候 在adapter中继承设置getItemViewType方法可以更有效率 。示例如下
.......
private static final int TYPE_ITEM
private static final int TYPE_SEPARATOR
private static final int TYPE_MAX_COUNT TYPE_SEPARATOR
Override
public int getItemViewType(int position) {
return mSeparatorsSet.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
}
Override
public int getViewTypeCount() {
return TYPE_MAX_COUNT;
}
Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder null;
int type getItemViewType(position);
if (convertView null) {
holder new ViewHolder();
switch (type) {
case TYPE_ITEM:
convertView mInflater.inflate(R.layout.item1, null);
holder.textView (TextView)convertView.findViewById......;
break;
case TYPE_SEPARATOR:
convertView mInflater.inflate(R.layout.item2, null);
holder.textView (TextView)convertView.findViewById......;
break;
}
convertView.setTag(holder);
} else {
holder (ViewHolder)convertView.getTag();
}
........
}
如果实现了RecyclerListener接口 当一个View由于ListView的滑动被系统回收到RecycleBin的mScrapViews数组时 会调用RecyclerListener中的onMovedToScrapHeap(View view)方法。RecycleBin相当于一个临时存储不需要显示的那部分Views的对象 随着列表滑动 这些Views需要显示出来的时候 他们就被从RecycleBin中拿了出来 RecycleBin本身并不对mScrapViews中的对象做回收操作。
于是在工程里 为ListView添加RecyclerListener接口 并在onMovedToScrapHeap方法中释放ListItem包含的Bitmap资源 这样可以极大的减少内存占用。
4.2 TransientStateView
用来标记这个view的瞬时状态 用来告诉app无需关心其保存和恢复。从注释看 这种具有瞬时状态的view 用于在view动画播放等情况中。
本文链接: http://scraponline.immuno-online.com/view-757000.html