【Android】View绘制过程分析之layout

来源:互联网 发布:java程序员转行 编辑:程序博客网 时间:2024/06/16 02:46

续前文“【Android】View绘制过程分析之measure”,继续分析View的绘制过程。

本文分析第2阶段,分析过程的注释标记在以下代码中。

/** * 此方法来给此View及它的子View分配大小和位置, * 子类不要覆写此方法,而覆写onLayout(boolean, int, int, int, int)方法即可 * 左上右下4个参数,表示此View相对于父View的位置 */public void layout(int l, int t, int r, int b) {    int oldL = mLeft;    int oldT = mTop;    int oldB = mBottom;    int oldR = mRight;    boolean changed = isLayoutModeOptical(mParent) ?            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {    //我们通过这个方法来分配大小和位置        onLayout(changed, l, t, r, b);        mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;        ListenerInfo li = mListenerInfo;        if (li != null && li.mOnLayoutChangeListeners != null) {            ArrayList<OnLayoutChangeListener> listenersCopy =                    (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();            int numListeners = listenersCopy.size();            for (int i = 0; i < numListeners; ++i) {                listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);            }        }    }    mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;}/** * 此方法给子View分配大小和位置。 * 应该做的事情:遍历子View, 调用子View的layout(int, int, int,int)方法给子View分配大小和位置  * 左上右下4个参数,表示此View相对于父View的位置 */protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}/** * View类中空实现这个方法,留给子类去实现。 * 下面看一下FrameLayout类的实现代码。 */protected void onLayout(boolean changed, int left, int top, int right, int bottom) {    final int count = getChildCount();        /**     * 下面4个方法:     * 若不考虑padding,则以下4个值应为:     * parentLeft = 0;  parentRight = right-left; parentTop = 0; parentBottom = bottom - top;     * 前面说到onLayout传入的左上右下4个参数是此View相对于父View的位置,那么right-left即求得此View所占的宽度,bottom-top即求得此View所点的高度     * 而这4个值构成的矩形范围,正是此View的占用范围     */    final int parentLeft = getPaddingLeftWithForeground();    final int parentRight = right - left - getPaddingRightWithForeground();    final int parentTop = getPaddingTopWithForeground();    final int parentBottom = bottom - top - getPaddingBottomWithForeground();    mForegroundBoundsChanged = true;        //遍历子View,计算子View的位置    for (int i = 0; i < count; i++) {        final View child = getChildAt(i);        if (child.getVisibility() != GONE) {            final LayoutParams lp = (LayoutParams) child.getLayoutParams();            //子View的大小在onMeasure(int, int)方法中已经计算得到            final int width = child.getMeasuredWidth();            final int height = child.getMeasuredHeight();            int childLeft;            int childTop;            int gravity = lp.gravity;            if (gravity == -1) {                gravity = DEFAULT_CHILD_GRAVITY;            }            final int layoutDirection = getLayoutDirection();            final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);            final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;            switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {                case Gravity.LEFT:                    childLeft = parentLeft + lp.leftMargin;                    break;                case Gravity.CENTER_HORIZONTAL:                    childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +                    lp.leftMargin - lp.rightMargin;                    break;                case Gravity.RIGHT:                    childLeft = parentRight - width - lp.rightMargin;                    break;                default:                    childLeft = parentLeft + lp.leftMargin;            }            switch (verticalGravity) {                case Gravity.TOP:                    childTop = parentTop + lp.topMargin;                    break;                case Gravity.CENTER_VERTICAL:                    childTop = parentTop + (parentBottom - parentTop - height) / 2 +                    lp.topMargin - lp.bottomMargin;                    break;                case Gravity.BOTTOM:                    childTop = parentBottom - height - lp.bottomMargin;                    break;                default:                    childTop = parentTop + lp.topMargin;            }            //调用子View的layout(int,int,int,int), 上面计算得到的4个值确定了子View的位置范围            child.layout(childLeft, childTop, childLeft + width, childTop + height);        }    }}

@容新华技术博客 - http://blog.csdn.net/rongxinhua - 原创文章,转载请注明出处


0 0