Android ListView初始化简单分析

下面是分析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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值