首页 » Android » Android视图绘制流程之layout

Android视图绘制流程之layout

原文 http://blog.csdn.net/qq_36523667/article/details/79253260

2018-02-05 02:00:40阅读(364)

借鉴自开发艺术


layout是view group用来确定子元素的位置的东西。

当ViewGroup的位置被确定后,它在onLayout中会遍历所有的子元素并调用其layout方法。


先看view的layout

@SuppressWarnings({"unchecked"})
public void layout(int l, int t, int r, int b) {
    if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
        onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
        mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    }
    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);
        if (shouldDrawRoundScrollbar()) {
            if(mRoundScrollbarRenderer == null) {
                mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
            }
        } else {
            mRoundScrollbarRenderer = null;
        }
        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;
    mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
    if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {
        mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
        notifyEnterOrExitForAutoFillIfNeeded(true);
    }
}
先通过setFrame方法来设定View的4个顶点的位置,即初始化mLeft、mRight、mTop、mBottom,View的4个顶点的位置一旦确定,那么View在父容器的位置也确定了。接着会调用onLayout方法。这个方法是父容器确定子元素的位置,调用父view group的回调。


LinearLayout的onLayout方法

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if (mOrientation == VERTICAL) {
        layoutVertical(l, t, r, b);
    } else {
        layoutHorizontal(l, t, r, b);
    }
}

layoutVertical

final int count = getVirtualChildCount();
for (int i = 0; i < count; i++) {
    final View child = getVirtualChildAt(i);
    if (child == null) {
        childTop += measureNullChild(i);
    } else if (child.getVisibility() != GONE) {
        final int childWidth = child.getMeasuredWidth();
        final int childHeight = child.getMeasuredHeight();
        final LinearLayout.LayoutParams lp =
                (LinearLayout.LayoutParams) child.getLayoutParams();
        ...
        if (hasDividerBeforeChildAt(i)) {
            childTop += mDividerHeight;
        }
        childTop += lp.topMargin;
        setChildFrame(child, childLeft, childTop + getLocationOffset(child),
                childWidth, childHeight);
        childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
        i += getChildrenSkipCount(child, i);
    }
}
这个方法会遍历所有的子元素并调用setChildFrame方法来为子元素确定对应的位置,其中childTop会逐渐增大,这就意味着后面的子元素会被放置在靠下的位置。所以他的一级级往下放不是空穴来风的,而是自己计算了下一个子元素的位置,并且防止在那个位置。

private void setChildFrame(View child, int left, int top, int width, int height) {
    child.layout(left, top, left + width, top + height);
}

这个顺序是view的layout-自身的view的onLayout(如果有的话)-遍历view的layout...


@ViewDebug.ExportedProperty(category = "layout")
public final int getHeight() {
    return mBottom - mTop;
}
这里才是获取了view实际的宽高


有两种情况,getHeight和getMeasureHeight会有不同

1.重写layout

@Override
public void layout(int l, int t, int r, int b) {
    super.layout(l, t, r + 100, b + 100);
}
2.测量过程中,有几次可能和得出的最终宽高不一致,不过最终来说,测量宽高和最终宽高还是一致的。

最新发布

CentOS专题

关于本站

5ibc.net旗下博客站精品博文小部分原创、大部分从互联网收集整理。尊重作者版权、传播精品博文,让更多编程爱好者知晓!

小提示

按 Ctrl+D 键,
把本文加入收藏夹