码迷,mamicode.com
首页 > 移动开发 > 详细

Android ListView初始化简单分析

时间:2015-04-04 10:31:40      阅读:246      评论:0      收藏:0      [点我收藏+]

标签:

下面是分析ListView初始化的源码流程分析,主要是ListVIew.onLayout过程与普通视图的layout过程完全不同,避免流程交代不清楚,以下是一个流程的思维导图。

     思维导图是顺序是从左向右,从上向下。

技术分享

一、 先看构造函数,上图中1.1就不分析了,主要是读取一些ListView参数,直接来看1.2 ViewGroup构造函数源码

private void initViewGroup() {  
    ......  
    // 初始化保存当前ViewGroup中所有View的数组  
    mChildren = new View[ARRAY_INITIAL_CAPACITY];  
    // 初始时其Child个数为0  
    mChildrenCount = 0;  
    ......  
}  

视图的创建过程的都会执行的三个步骤: onMeasure, onLayout, onDraw

 

二、接着2 即 ListView.onMeasure方法,只是获取当前ListView的宽高

@Override  
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    // Sets up mListPadding  
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  
    // 获取当前ListView总宽高  
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  
    ......  
  
    setMeasuredDimension(widthSize , heightSize);  
    mWidthMeasureSpec = widthMeasureSpec;          
}  

三、步骤3是重点,AbsListView.onLayout的流程与普通View的不同

@Override  
protected void onLayout(boolean changed, int l, int t, int r, int b) {  
    super.onLayout(changed, l, t, r, b);  
    mInLayout = true;  
    // 初始时changed = true  
    if (changed) {  
        int childCount = getChildCount();  
        for (int i = 0; i < childCount; i++) {  
            // ?  
            getChildAt(i).forceLayout();  
        }  
        mRecycler.markChildrenDirty();  
    }  
      
    if (mFastScroller != null && mItemCount != mOldItemCount) {  
        mFastScroller.onItemCountChanged(mOldItemCount, mItemCount);  
    }  
  
    // ListView实现此方法  
    layoutChildren();  
    mInLayout = false;  
  
    mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;  
} 

四、步骤4.1 具体分析ListVIew.layoutChildren

@Override  
protected void layoutChildren() {  
    // 默认为false  
    final boolean blockLayoutRequests = mBlockLayoutRequests;  
    if (!blockLayoutRequests) {  
        // 目的是为了阻止没必要的layout操作,提交效率  
        mBlockLayoutRequests = true;  
    } else {  
        return;  
    }  
  
    try {  
        super.layoutChildren();  
        // 为什么此处要请求重绘?  
        invalidate();  
  
        ......  
  
        int childCount = getChildCount();  
  
        ......  
  
        boolean dataChanged = mDataChanged;  
        if (dataChanged) {  
            handleDataChanged();  
        }  
  
        ......  
  
        // 把所有child view放置到RecycleBin  
        // 满足条件的话可以重用这些child view  
        final int firstPosition = mFirstPosition;  
          
        // ListView中Item复用使用此数据结构  
        final RecycleBin recycleBin = mRecycler;  
  
        // reset the focus restoration  
        View focusLayoutRestoreDirectChild = null;  
  
        // Don‘t put header or footer views into the Recycler. Those are  
        // already cached in mHeaderViews;  
        if (dataChanged) {  
            for (int i = 0; i < childCount; i++) {  
                recycleBin.addScrapView(getChildAt(i), firstPosition+i);  
            }  
        } else {  
            // 创建childCount个数的View放入RecycleBin类activeViews数组中  
            recycleBin.fillActiveViews(childCount, firstPosition);  
        }  
  
        ......  
  
        // Clear out old views  
        detachAllViewsFromParent();  
        recycleBin.removeSkippedScrap();  
  
        switch (mLayoutMode) {  
          
        ......  
          
        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();  
  
         ......  
  
        invokeOnItemScrollListener();  
    } finally {  
        if (!blockLayoutRequests) {  
            mBlockLayoutRequests = false;  
        }  
    }

五、 分析步骤4.2 ListView.fillFromTop源码

// 从上向下在ListView中填充Item View  
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);  
   } 

六、查看步骤4.3 ListView.fillDown源码

// 在参数pos下面填充Item View  
private View fillDown(int pos, int nextTop) {  
    View selectedView = null;  
  
    // ListView getHeight也是这样计算的  
    int end = (mBottom - mTop);  
    if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {  
        end -= mListPadding.bottom;  
    }  
  
    // 初始化时pos = 0,如果item总数少于一屏幕,执行mItemCount - pos次  
    // 如果item多余一屏幕,执行end - nextTop次  
    while (nextTop < end && pos < mItemCount) {  
        // is this the selected item?  
        boolean selected = pos == mSelectedPosition;  
        // 获取Item 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;  
}

 

七、查看步骤4.4 ListView.makeAndAddView源码

 

// ListView都是通过此方法获取Item View  
// 具体Item View如何复用,是否需要创建新的Item View都有此方法处理  
private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,  
        boolean selected) {  
    View child;  
  
 ListView的数据发生变化,肯定Item View之前已经创建好了,无需重新创建  
    if (!mDataChanged) {  
        // 当前position复用之前创建的视图  
        child = mRecycler.getActiveView(position);  
        if (child != null) {  
            // 对复用的View针对当前需要进行配置  
            setupChild(child, position, y, flow, childrenLeft, selected, true);  
  
            return child;  
        }  
    }  
  
    // 创建或者重用视图  
    child = obtainView(position, mIsScrap);  
  
    // This needs to be positioned and measured  
    setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);  
  
    return child;  
}  

八 查看步骤4.5 ListView.setupChild源码

 

private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft,  
        boolean selected, boolean recycled) {  
    // 判断当前Item View是否选中状态  
    final boolean isSelected = selected && shouldShowSelector();  
    final boolean updateChildSelected = isSelected != child.isSelected();  
      
    final int mode = mTouchMode;  
      
    // 是否处于按下状态  
    final boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLL &&  
            mMotionPosition == position;  
    final boolean updateChildPressed = isPressed != child.isPressed();  
      
    // 是否需要重新measure与layout  
    final boolean needToMeasure = !recycled || updateChildSelected || child.isLayoutRequested();  
  
    // Respect layout params that are already in the view. Otherwise make some up...  
    // noinspection unchecked  
    AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams();  
    if (p == null) {  
        p = (AbsListView.LayoutParams) generateDefaultLayoutParams();  
    }  
    p.viewType = mAdapter.getItemViewType(position);  
  
    if ((recycled && !p.forceAdd) || (p.recycledHeaderFooter &&  
            p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) {  
        attachViewToParent(child, flowDown ? -1 : 0, p);  
    } else {  
        p.forceAdd = false;  
        if (p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {  
            p.recycledHeaderFooter = true;  
        }  
        // 向ListView(ViewGroup子类)添加当前Item View  
        addViewInLayout(child, flowDown ? -1 : 0, p, true);  
    }  
  
    // 更新选中状态  
    if (updateChildSelected) {  
        child.setSelected(isSelected);  
    }  
  
    // 更新按下状态  
    if (updateChildPressed) {  
        child.setPressed(isPressed);  
    }  
  
    if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {  
        if (child instanceof Checkable) {  
            ((Checkable) child).setChecked(mCheckStates.get(position));  
        } else if (getContext().getApplicationInfo().targetSdkVersion  
                >= android.os.Build.VERSION_CODES.HONEYCOMB) {  
            child.setActivated(mCheckStates.get(position));  
        }  
    }  
  
    if (needToMeasure) {  
        int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,  
                mListPadding.left + mListPadding.right, p.width);  
        int lpHeight = p.height;  
        int childHeightSpec;  
        if (lpHeight > 0) {  
            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);  
        } else {  
            childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);  
        }  
        // 与普通视图的measure流程不同,ListView是在此处执行具体的当前Item View measure  
        child.measure(childWidthSpec, childHeightSpec);  
    } else {  
        cleanupLayoutState(child);  
    }  
  
    final int w = child.getMeasuredWidth();  
    final int h = child.getMeasuredHeight();  
    final int childTop = flowDown ? y : y - h;  
  
    if (needToMeasure) {  
        final int childRight = childrenLeft + w;  
        final int childBottom = childTop + h;  
        // 大小改变肯定位置也会发生变化,当前Item View重新进行layout  
        child.layout(childrenLeft, childTop, childRight, childBottom);  
    } else {  
        child.offsetLeftAndRight(childrenLeft - child.getLeft());  
        child.offsetTopAndBottom(childTop - child.getTop());  
    }  
  
    if (mCachingStarted && !child.isDrawingCacheEnabled()) {  
        child.setDrawingCacheEnabled(true);  
    }  
  
    if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition)  
            != position) {  
        child.jumpDrawablesToCurrentState();  
    }  
} 

 

Android ListView初始化简单分析

标签:

原文地址:http://www.cnblogs.com/duanxz/p/4391672.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!