ListView的刷新与复用
来源:互联网 发布:mac 财务管理软件 编辑:程序博客网 时间:2024/05/16 16:24
之前我们说过listView的刷新,最终调用的是view.requestLayout。然后经过一系列android机制,最后其实就是view的绘制过程。只其中的过程这里就不描述了,咱们直接看listView的绘制过程吧。
listView.onLayout
绘制的基本流程,onMeasure-->onLayout-->onDraw,对于listView,measure、draw都没什么特别的,终点在onLayout中(你就先这么记着)。
listView是继承了absListView的onLayout
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); mInLayout = true; final int childCount = getChildCount(); if (changed) { for (int i = 0; i < childCount; i++) { getChildAt(i).forceLayout(); } mRecycler.markChildrenDirty(); } layoutChildren(); mInLayout = false; mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR; // TODO: Move somewhere sane. This doesn't belong in onLayout(). if (mFastScroll != null) { mFastScroll.onItemCountChanged(getChildCount(), mItemCount); } }
核心方法是layoutChildren,它的具体实现又是在各自子类中实现的,根据方法名这个很好理解。
listView.layoutChildren:
protected void layoutChildren() { ...... // Pull all children into the RecycleBin. // These views will be reused if possible final int firstPosition = mFirstPosition; final RecycleBin recycleBin = mRecycler;//我们的主角出现了 //dataChanged 是adapterView的变量,在数据变化,即调用notifyDataChange的时候,为true if (dataChanged) { //childCount 屏幕内展示的item的个数(我的猜测) for (int i = 0; i < childCount; i++) { recycleBin.addScrapView(getChildAt(i), firstPosition+i); } } else { recycleBin.fillActiveViews(childCount, firstPosition); } // Clear out old views detachAllViewsFromParent(); recycleBin.removeSkippedScrap(); switch (mLayoutMode) { case LAYOUT_SET_SELECTION: if (newSel != null) { sel = fillFromSelection(newSel.getTop(), childrenTop, childrenBottom); } else { sel = fillFromMiddle(childrenTop, childrenBottom); } break; case LAYOUT_SYNC: sel = fillSpecific(mSyncPosition, mSpecificTop); break; case LAYOUT_FORCE_BOTTOM: sel = fillUp(mItemCount - 1, childrenBottom); adjustViewsUpOrDown(); break; case LAYOUT_FORCE_TOP: mFirstPosition = 0; sel = fillFromTop(childrenTop); adjustViewsUpOrDown(); break; case LAYOUT_SPECIFIC: sel = fillSpecific(reconcileSelectedPosition(), mSpecificTop); break; case LAYOUT_MOVE_SELECTION: sel = moveSelection(oldSel, newSel, delta, childrenTop, childrenBottom); break; default: if (childCount == 0) { if (!mStackFromBottom) { final int position = lookForSelectablePosition(0, true); setSelectedPositionInt(position); sel = fillFromTop(childrenTop); } else { final int position = lookForSelectablePosition(mItemCount - 1, false); setSelectedPositionInt(position); sel = fillUp(mItemCount - 1, childrenBottom); } } else { if (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) { sel = fillSpecific(mSelectedPosition, oldSel == null ? childrenTop : oldSel.getTop()); } else if (mFirstPosition < mItemCount) { sel = fillSpecific(mFirstPosition, oldFirst == null ? childrenTop : oldFirst.getTop()); } else { sel = fillSpecific(0, childrenTop); } } break; } // Flush any cached views that did not get reused above recycleBin.scrapActiveViews(); ......}
这个只列出了我们这次要分析的代码,其他部分需要的时候再看,毕竟太长了。
在listView首次展示的时候,dataChanged 为false,childCount==0,所以,recycleBin中ScrapView、ActiveViews都是空。接线来,mLayoutMode 默认是LAYOUT_NORMAL,所以走default分支。
mStackFromBottom是AbsListView的变量
/** * Indicates whether the list is stacked from the bottom edge or * the top edge. */ boolean mStackFromBottom;
因为布局是从上而下,所以mStackFromBottom==false,然后进fillFromTop方法;
/** * Fills the list from top to bottom, starting with mFirstPosition * * @param nextTop The location where the top of the first item should be drawn * * @return The view that is currently selected */ private View fillFromTop(int nextTop) { mFirstPosition = Math.min(mFirstPosition, mSelectedPosition); mFirstPosition = Math.min(mFirstPosition, mItemCount - 1); if (mFirstPosition < 0) { mFirstPosition = 0; } return fillDown(mFirstPosition, nextTop); }
从这里开始对listView进行填充
fillDown:
/*** Fills the list from pos down to the end of the list view. * * @param pos The first position to put in the list * * @param nextTop The location where the top of the item associated with pos * should be drawn * * @return The view that is currently selected, if it happens to be in the * range that we draw. */private View fillDown(int pos, int nextTop) { View selectedView = null; int end = (mBottom - mTop); if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) { end -= mListPadding.bottom; } while (nextTop < end && pos < mItemCount) { // is this the selected item? boolean selected = pos == mSelectedPosition; View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected); nextTop = child.getBottom() + mDividerHeight; if (selected) { selectedView = child; } pos++; } setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1); return selectedView;}
核心方法,makeAndAddView,while循环,遍历创建屏幕内的itemView。根据名字makeAndAddView就是创建item并把它添加到屏幕上
makeAndAddView:
/** * 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. * * @param position Logical position in the list * @param y Top or bottom edge of the view to add * @param flow If flow is true, align top edge to y. If false, align bottom * edge to y. * @param childrenLeft Left edge where children should be positioned * @param selected Is this position selected? * @return View that was added */private View makeAndAddView(int position, int y, boolean flow, int childrenLeft, boolean selected) { View child; if (!mDataChanged) { // 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 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;}
makeAndAddView是我们要讨论的listView之所以能item复用功能,最重要的方法。
当listView首次加载 or scroll的时候,mDataChanged==false,进入if判断。
getActiveView有可能返回null(目前知道的,只有在首次加载的第二次layout的时候不是null)。
如果ActiveView中没有,则进obtainView(这也是个很重要的方法),寻找可用的view。
obtainView:
/** * Get a view and have it show the data associated with the specified * position. This is called when we have already discovered that the view is * not available for reuse in the recycle bin. The only choices left are * converting an old view or making a new one. * * @param position The position to display * @param isScrap Array of at least 1 boolean, the first entry will become true if * the returned view was taken from the scrap heap, false if otherwise. * * @return A view displaying the data associated with the specified position */ View obtainView(int position, boolean[] isScrap) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView"); isScrap[0] = false; // Check whether we have a transient state view. Attempt to re-bind the // data and discard the view if we fail. final View transientView = mRecycler.getTransientStateView(position); if (transientView != null) { final LayoutParams params = (LayoutParams) transientView.getLayoutParams(); // If the view type hasn't changed, attempt to re-bind the data. if (params.viewType == mAdapter.getItemViewType(position)) { final View updatedView = mAdapter.getView(position, transientView, this); // If we failed to re-bind the data, scrap the obtained view. if (updatedView != transientView) { setItemViewLayoutParams(updatedView, position); mRecycler.addScrapView(updatedView, position); } } isScrap[0] = true; // Finish the temporary detach started in addScrapView(). transientView.dispatchFinishTemporaryDetach(); return transientView; } final View scrapView = mRecycler.getScrapView(position); final View child = mAdapter.getView(position, scrapView, this); if (scrapView != null) { if (child != scrapView) { // Failed to re-bind the data, return scrap to the heap. mRecycler.addScrapView(scrapView, position); } else { isScrap[0] = true; // Finish the temporary detach started in addScrapView(). child.dispatchFinishTemporaryDetach(); } } if (mCacheColorHint != 0) { child.setDrawingCacheBackgroundColor(mCacheColorHint); } if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) { child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); } setItemViewLayoutParams(child, position); if (AccessibilityManager.getInstance(mContext).isEnabled()) { if (mAccessibilityDelegate == null) { mAccessibilityDelegate = new ListItemAccessibilityDelegate(); } if (child.getAccessibilityDelegate() == null) { child.setAccessibilityDelegate(mAccessibilityDelegate); } } Trace.traceEnd(Trace.TRACE_TAG_VIEW); return child; }
通过obtainView返回最后的item,中间的关键是调用adapter.getView(这个我们应该都很熟悉了,写adapter的时候必须要重写的),在getView里面选择使用重用view。然后判断getView的返回和recycle的scapView是否一致,如果不一致,更新scapView(猜测)。
- ListView的刷新与复用
- ListView的优化与刷新
- listview刷新与内部控件的监听
- Listview的下拉刷新
- listView的下拉刷新
- listview的局部刷新
- ListView的局部刷新
- ListView的局部刷新
- listview的局部刷新
- ListView的局部刷新
- listview的局部刷新
- ListView的刷新
- ListView的局部刷新
- ListView的局部刷新
- ListView 的局部刷新
- ListView的单项刷新
- ListView的单项刷新
- listView的下拉刷新
- solr5.5+tomcat8+zookeeper
- 隐函数存在定理
- (安卓)RecyclerView 多条目加载 主要代码
- 【Scikit-Learn 中文文档】最近邻
- 爬虫小技巧京东
- ListView的刷新与复用
- select onchange方法传值
- JDBC 连接Hive 简单样例(开启Kerberos)
- uva12169(模算数)
- HDOJ 小希的迷宫 JAVA 1272
- JFreeChart创建区域图
- MySQL数据库入门
- 数据库备份和恢复操作
- 自定义组合控件实现 购物车加减的简单实现