Android ListView初始化簡單分析

yangxi_001發表於2017-07-31

下面是分析ListView初始化的原始碼流程分析,主要是ListVIew.onLayout過程與普通檢視的layout過程完全不同,避免流程交代不清楚,以下是一個流程的思維導圖。

     思維導圖是順序是從左向右,從上向下。




一、 先看建構函式,上圖中1.1就不分析了,主要是讀取一些ListView引數,直接來看1.2 ViewGroup建構函式原始碼

[java] view plain copy
 print?
  1. private void initViewGroup() {  
  2.     ......  
  3.     // 初始化儲存當前ViewGroup中所有View的陣列  
  4.     mChildren = new View[ARRAY_INITIAL_CAPACITY];  
  5.     // 初始時其Child個數為0  
  6.     mChildrenCount = 0;  
  7.     ......  
  8. }  



檢視的建立過程的都會執行的三個步驟: onMeasure, onLayout, onDraw


二、接著2 即 ListView.onMeasure方法,只是獲取當前ListView的寬高

[java] view plain copy
 print?
  1. @Override  
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  3.     // Sets up mListPadding  
  4.     super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  5.   
  6.     // 獲取當前ListView總寬高  
  7.     int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  8.     int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  9.   
  10.     ......  
  11.   
  12.     setMeasuredDimension(widthSize , heightSize);  
  13.     mWidthMeasureSpec = widthMeasureSpec;          
  14. }  

三、步驟3是重點,AbsListView.onLayout的流程與普通View的不同

[java] view plain copy
 print?
  1. @Override  
  2. protected void onLayout(boolean changed, int l, int t, int r, int b) {  
  3.     super.onLayout(changed, l, t, r, b);  
  4.     mInLayout = true;  
  5.     // 初始時changed = true  
  6.     if (changed) {  
  7.         int childCount = getChildCount();  
  8.         for (int i = 0; i < childCount; i++) {  
  9.             // ?  
  10.             getChildAt(i).forceLayout();  
  11.         }  
  12.         mRecycler.markChildrenDirty();  
  13.     }  
  14.       
  15.     if (mFastScroller != null && mItemCount != mOldItemCount) {  
  16.         mFastScroller.onItemCountChanged(mOldItemCount, mItemCount);  
  17.     }  
  18.   
  19.     // ListView實現此方法  
  20.     layoutChildren();  
  21.     mInLayout = false;  
  22.   
  23.     mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;  
  24. }  

四、步驟4.1 具體分析ListVIew.layoutChildren

[java] view plain copy
 print?
  1. @Override  
  2. protected void layoutChildren() {  
  3.     // 預設為false  
  4.     final boolean blockLayoutRequests = mBlockLayoutRequests;  
  5.     if (!blockLayoutRequests) {  
  6.         // 目的是為了阻止沒必要的layout操作,提交效率  
  7.         mBlockLayoutRequests = true;  
  8.     } else {  
  9.         return;  
  10.     }  
  11.   
  12.     try {  
  13.         super.layoutChildren();  
  14.         // 為什麼此處要請求重繪?  
  15.         invalidate();  
  16.   
  17.         ......  
  18.   
  19.         int childCount = getChildCount();  
  20.   
  21.         ......  
  22.   
  23.         boolean dataChanged = mDataChanged;  
  24.         if (dataChanged) {  
  25.             handleDataChanged();  
  26.         }  
  27.   
  28.         ......  
  29.   
  30.         // 把所有child view放置到RecycleBin  
  31.         // 滿足條件的話可以重用這些child view  
  32.         final int firstPosition = mFirstPosition;  
  33.           
  34.         // ListView中Item複用使用此資料結構  
  35.         final RecycleBin recycleBin = mRecycler;  
  36.   
  37.         // reset the focus restoration  
  38.         View focusLayoutRestoreDirectChild = null;  
  39.   
  40.         // Don't put header or footer views into the Recycler. Those are  
  41.         // already cached in mHeaderViews;  
  42.         if (dataChanged) {  
  43.             for (int i = 0; i < childCount; i++) {  
  44.                 recycleBin.addScrapView(getChildAt(i), firstPosition+i);  
  45.             }  
  46.         } else {  
  47.             // 建立childCount個數的View放入RecycleBin類activeViews陣列中  
  48.             recycleBin.fillActiveViews(childCount, firstPosition);  
  49.         }  
  50.   
  51.         ......  
  52.   
  53.         // Clear out old views  
  54.         detachAllViewsFromParent();  
  55.         recycleBin.removeSkippedScrap();  
  56.   
  57.         switch (mLayoutMode) {  
  58.           
  59.         ......  
  60.           
  61.         default:  
  62.             if (childCount == 0) {  
  63.                 if (!mStackFromBottom) {  
  64.                     final int position = lookForSelectablePosition(0true);  
  65.                     setSelectedPositionInt(position);  
  66.                     // 此方法是重點,以下具體分析  
  67.                     sel = fillFromTop(childrenTop);  
  68.                 } else {  
  69.                     final int position = lookForSelectablePosition(mItemCount - 1false);  
  70.                     setSelectedPositionInt(position);  
  71.                     sel = fillUp(mItemCount - 1, childrenBottom);  
  72.                 }  
  73.             } else {  
  74.                 if (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) {  
  75.                     sel = fillSpecific(mSelectedPosition,  
  76.                             oldSel == null ? childrenTop : oldSel.getTop());  
  77.                 } else if (mFirstPosition < mItemCount) {  
  78.                     sel = fillSpecific(mFirstPosition,  
  79.                             oldFirst == null ? childrenTop : oldFirst.getTop());  
  80.                 } else {  
  81.                     sel = fillSpecific(0, childrenTop);  
  82.                 }  
  83.             }  
  84.             break;  
  85.         }  
  86.   
  87.         // Flush any cached views that did not get reused above  
  88.         recycleBin.scrapActiveViews();  
  89.   
  90.          ......  
  91.   
  92.         invokeOnItemScrollListener();  
  93.     } finally {  
  94.         if (!blockLayoutRequests) {  
  95.             mBlockLayoutRequests = false;  
  96.         }  
  97.     }  


五、 分析步驟4.2 ListView.fillFromTop原始碼

[java] view plain copy
 print?
  1. // 從上向下在ListView中填充Item View  
  2. private View fillFromTop(int nextTop) {  
  3.        mFirstPosition = Math.min(mFirstPosition, mSelectedPosition);  
  4.        mFirstPosition = Math.min(mFirstPosition, mItemCount - 1);  
  5.        if (mFirstPosition < 0) {  
  6.            mFirstPosition = 0;  
  7.        }  
  8.        // 具體操作在此  
  9.        return fillDown(mFirstPosition, nextTop);  
  10.    }  

六、檢視步驟4.3 ListView.fillDown原始碼

[java] view plain copy
 print?
  1. // 在引數pos下面填充Item View  
  2. private View fillDown(int pos, int nextTop) {  
  3.     View selectedView = null;  
  4.   
  5.     // ListView getHeight也是這樣計算的  
  6.     int end = (mBottom - mTop);  
  7.     if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {  
  8.         end -= mListPadding.bottom;  
  9.     }  
  10.   
  11.     // 初始化時pos = 0,如果item總數少於一螢幕,執行mItemCount - pos次  
  12.     // 如果item多餘一螢幕,執行end - nextTop次  
  13.     while (nextTop < end && pos < mItemCount) {  
  14.         // is this the selected item?  
  15.         boolean selected = pos == mSelectedPosition;  
  16.         // 獲取Item View物件  
  17.         View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected);  
  18.   
  19.         nextTop = child.getBottom() + mDividerHeight;  
  20.         if (selected) {  
  21.             selectedView = child;  
  22.         }  
  23.         pos++;  
  24.     }  
  25.   
  26.     setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1);  
  27.     return selectedView;  
  28. }  


七、檢視步驟4.4 ListView.makeAndAddView原始碼

[java] view plain copy
 print?
  1. // ListView都是通過此方法獲取Item View  
  2. // 具體Item View如何複用,是否需要建立新的Item View都有此方法處理  
  3. private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,  
  4.         boolean selected) {  
  5.     View child;  
  6.   
  7.  ListView的資料發生變化,肯定Item View之前已經建立好了,無需重新建立  
  8.     if (!mDataChanged) {  
  9.         // 當前position複用之前建立的檢視  
  10.         child = mRecycler.getActiveView(position);  
  11.         if (child != null) {  
  12.             // 對複用的View針對當前需要進行配置  
  13.             setupChild(child, position, y, flow, childrenLeft, selected, true);  
  14.   
  15.             return child;  
  16.         }  
  17.     }  
  18.   
  19.     // 建立或者重用檢視  
  20.     child = obtainView(position, mIsScrap);  
  21.   
  22.     // This needs to be positioned and measured  
  23.     setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);  
  24.   
  25.     return child;  
  26. }  

八 檢視步驟4.5 ListView.setupChild原始碼

[java] view plain copy
 print?
  1. private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft,  
  2.         boolean selected, boolean recycled) {  
  3.     // 判斷當前Item View是否選中狀態  
  4.     final boolean isSelected = selected && shouldShowSelector();  
  5.     final boolean updateChildSelected = isSelected != child.isSelected();  
  6.       
  7.     final int mode = mTouchMode;  
  8.       
  9.     // 是否處於按下狀態  
  10.     final boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLL &&  
  11.             mMotionPosition == position;  
  12.     final boolean updateChildPressed = isPressed != child.isPressed();  
  13.       
  14.     // 是否需要重新measure與layout  
  15.     final boolean needToMeasure = !recycled || updateChildSelected || child.isLayoutRequested();  
  16.   
  17.     // Respect layout params that are already in the view. Otherwise make some up...  
  18.     // noinspection unchecked  
  19.     AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams();  
  20.     if (p == null) {  
  21.         p = (AbsListView.LayoutParams) generateDefaultLayoutParams();  
  22.     }  
  23.     p.viewType = mAdapter.getItemViewType(position);  
  24.   
  25.     if ((recycled && !p.forceAdd) || (p.recycledHeaderFooter &&  
  26.             p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) {  
  27.         attachViewToParent(child, flowDown ? -1 : 0, p);  
  28.     } else {  
  29.         p.forceAdd = false;  
  30.         if (p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {  
  31.             p.recycledHeaderFooter = true;  
  32.         }  
  33.         // 向ListView(ViewGroup子類)新增當前Item View  
  34.         addViewInLayout(child, flowDown ? -1 : 0, p, true);  
  35.     }  
  36.   
  37.     // 更新選中狀態  
  38.     if (updateChildSelected) {  
  39.         child.setSelected(isSelected);  
  40.     }  
  41.   
  42.     // 更新按下狀態  
  43.     if (updateChildPressed) {  
  44.         child.setPressed(isPressed);  
  45.     }  
  46.   
  47.     if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {  
  48.         if (child instanceof Checkable) {  
  49.             ((Checkable) child).setChecked(mCheckStates.get(position));  
  50.         } else if (getContext().getApplicationInfo().targetSdkVersion  
  51.                 >= android.os.Build.VERSION_CODES.HONEYCOMB) {  
  52.             child.setActivated(mCheckStates.get(position));  
  53.         }  
  54.     }  
  55.   
  56.     if (needToMeasure) {  
  57.         int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,  
  58.                 mListPadding.left + mListPadding.right, p.width);  
  59.         int lpHeight = p.height;  
  60.         int childHeightSpec;  
  61.         if (lpHeight > 0) {  
  62.             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);  
  63.         } else {  
  64.             childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);  
  65.         }  
  66.         // 與普通檢視的measure流程不同,ListView是在此處執行具體的當前Item View measure  
  67.         child.measure(childWidthSpec, childHeightSpec);  
  68.     } else {  
  69.         cleanupLayoutState(child);  
  70.     }  
  71.   
  72.     final int w = child.getMeasuredWidth();  
  73.     final int h = child.getMeasuredHeight();  
  74.     final int childTop = flowDown ? y : y - h;  
  75.   
  76.     if (needToMeasure) {  
  77.         final int childRight = childrenLeft + w;  
  78.         final int childBottom = childTop + h;  
  79.         // 大小改變肯定位置也會發生變化,當前Item View重新進行layout  
  80.         child.layout(childrenLeft, childTop, childRight, childBottom);  
  81.     } else {  
  82.         child.offsetLeftAndRight(childrenLeft - child.getLeft());  
  83.         child.offsetTopAndBottom(childTop - child.getTop());  
  84.     }  
  85.   
  86.     if (mCachingStarted && !child.isDrawingCacheEnabled()) {  
  87.         child.setDrawingCacheEnabled(true);  
  88.     }  
  89.   
  90.     if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition)  
  91.             != position) {  
  92.         child.jumpDrawablesToCurrentState();  
  93.     }  
  94. }  


轉載請註明原文地址:http://blog.csdn.net/love_world_/article/details/8547077

相關文章