//寬度測量邏輯 if (widthMode == MeasureSpec.UNSPECIFIED) { widthSize = mListPadding.left + mListPadding.right + childWidth + getVerticalScrollbarWidth(); } else { //初始化childState = combineMeasuredStates(childState, child.getMeasuredState()) widthSize |= (childState & MEASURED_STATE_MASK); } //高度測量邏輯,這裏若是測量模式爲UNSPECIFIED,listview的高度就只會顯示一個childHeight的高度 if (heightMode == MeasureSpec.UNSPECIFIED) { //初始化 childHeight = child.getMeasuredHeight(); heightSize = mListPadding.top + mListPadding.bottom + childHeight + getVerticalFadingEdgeLength() * 2; } //正常狀況下測量模式應該是AT_MOST,此時會去累加每個children的高度 if (heightMode == MeasureSpec.AT_MOST) { heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1); }
listview經過RecycleBin進行view的複用.複用的時機在layoutChildren中進行java
2.1 RecycleBin原理數組
RecycleBin兩個比較重要的view數組緩存
//存儲當前顯示的view內容 private View[] mActiveViews = new View[0]; //根據不一樣的type廢棄的viewlist private ArrayList<View>[] mScrapViews;
在listview的layoutChildren()方法中 RecycleBin初始化mActiveViews方式markdown
if (dataChanged) { for (int i = 0; i < childCount; i++) { //數據改變後把view放置廢棄的複用池裏 recycleBin.addScrapView(getChildAt(i), firstPosition+i); } } else { //childCount爲有效顯示的child數量 recycleBin.fillActiveViews(childCount, firstPosition); } ... //將目前全部的ActiveViews降級爲ScrapViews,並將以前的全部ScrapViews清除,爲新產生的 //ActiveViews作好準備 recycleBin.scrapActiveViews();
RecycleBin複用過程 :
與用戶進行交互的View,那麼這些View會經過RecycleBin直接存儲到mActivityView數組當中,以便爲了直接複用. 當咱們滑動ListView的時候,有些View被滑動到屏幕以外(offScreen) View,那麼這些View就成爲了ScrapView,也就是廢棄的View,已經沒法與用戶進行交互了,這樣在UI視圖改變的時候就沒有繪製這些無用視圖的必要了。他將會被RecycleBin存儲到mScrapView數組當中,可是沒有被銷燬掉,目的是爲了二次複用,也就是間接複用。當新的View須要顯示的時候,先判斷mActivityView中是否存在,若是存在那麼咱們就能夠從mActivityView數組當中直接取出複用,也就是直接複用,不然的話從mScrapView數組當中進行判斷,若是存在,那麼二次複用當前的視圖,若是不存在,那麼就須要inflate View了。網絡
搬挪自網絡的總結圖片:ide
獲取view時複用的代碼邏輯以下: 佈局
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邏輯方法 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; }
private View makeAndAddView(int position, int y, boolean flow, int childrenLeft, boolean selected) { if (!mDataChanged) { //獲取ActiveView邏輯 // Try to use an existing view for this position. final View activeView = mRecycler.getActiveView(position); if (activeView != null) { // Found it. We're reusing an existing child, so it just needs // to be positioned like a scrap view. setupChild(activeView, position, y, flow, childrenLeft, selected, true); return activeView; } } // Make a new view for this position, or convert an unused view if // possible. /** *若是mActivityView[]數組中沒有可用的View,那麼嘗試從mScrapView數組中讀取.而後從新佈局. *若是能夠從mScrapView數組中能夠獲取到,那麼直接返回調用mAdapter.getView(position,scrapView,this); *若是獲取不到那麼執行mAdapter.getView(position,null,this)方法. */ final View child = obtainView(position, mIsScrap); // This needs to be positioned and measured. setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]); return child; }
這裏能夠看到若是數據源沒有變化的時候,會從mActivityView數組中判斷是否存在能夠直接複用的View,可能不少讀者都不太明白直接複用究竟是怎麼個過程,舉個例子,好比說咱們ListView一頁能夠顯示10條數據,那麼咱們在這個時候滑動一個Item的距離,也就是說把position = 0的Item移除屏幕,將position = 10 的Item移入屏幕,那麼position = 1的Item是否是就直接可以從mActivityView數組中拿到呢?這是能夠的,咱們在第一次加載Item數據的時候,已經將position = 0~9的Item加入到了mActivityView數組當中,那麼在第二次加載的時候,因爲position = 1 的Item仍是ActivityView,那麼這裏就能夠直接從數組中獲取,而後從新佈局。這裏也就表示的是Item的直接複用。post
若是咱們在mActivityView數組中獲取不到position對應的View,那麼就嘗試從mScrapView廢棄View數組中嘗試去獲取,還拿剛纔的例子來講當position = 0的Item被移除屏幕的時候,首先會Detach讓View和視圖進行分離,清空children,而後將廢棄View添加到mScrapView數組當中,當加載position = 10的Item時,mActivityView數組確定是沒有的,也就沒法獲取到,一樣mScrapView中也是不存在postion = 10與之對應的廢棄View,說白了就是mScrapView數組只有mScrapView[0]這一項數據,確定是沒有mScrapView[10]這項數據的,那麼咱們就會這樣想,確定是從Adapter中的getView方法獲取新的數據嘍,其實並非這樣,雖然mScrapView中雖然沒有與之對應的廢棄View,可是會返回最後一個緩存的View傳遞給convertview。那麼也就是將mScrapView[0]對應的View返回。整體的流程就是這樣。this
抄自網絡圖片:
spa