View的绘制流程分析之三 -- layout

layout - 布局

确定View的最终宽高以及四个顶点的位置!

接着上一篇 View的绘制流程分析之二 – measure 往下分析layout过程!

在ViewRootImpl 中的performTraversals() 函数内部,执行performMeasure() 完毕之后,

// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// ...

layoutRequested = true;
  • 1
  • 2
  • 3
  • 4
  • 5
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
        boolean triggerGlobalLayoutListener = didLayout
                || mAttachInfo.mRecomputeGlobalAttributes;
        if (didLayout) {
            performLayout(lp, mWidth, mHeight); // 开始布局过程!

            // ...
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
        // ... 
        final View host = mView;
        try {
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
            // ... 
            }
        } finally {
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

host就是那个DecorView对象!

然后又走到了View中的 layout()方法!

public void layout(int l, int t, int r, int b) {
        // ...

        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);

            // ... 
        }

        // ...
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在layout() 内部调用了onLayout() 函数!

onLayout() 在View.java中是一个空实现!

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    }
  • 1
  • 2

来看ViewGroup中

@Override
    protected abstract void onLayout(boolean changed,
            int l, int t, int r, int b);
  • 1
  • 2
  • 3

也是空实现,变成了一个抽象方法!!!

其实,不同的ViewGroup对象,有不同的展现形式,所以需要在不同的ViewGroup对象实现各自的onLayout() 函数!

还是那FrameLayout来举例!

@Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        layoutChildren(left, top, right, bottom, false /* no force left gravity */);
    }
  • 1
  • 2
  • 3
  • 4
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
        // 1. 获取子view的个数
        final int count = getChildCount();
        // 2. FrameLayout的左侧padding值
        final int parentLeft = getPaddingLeftWithForeground();
        // 3. FrameLayout的右侧padding值
        final int parentRight = right - left - getPaddingRightWithForeground();
        // 4. FrameLayout的顶部padding值
        final int parentTop = getPaddingTopWithForeground();
        // 5. FrameLayout的底侧padding值
        final int parentBottom = bottom - top - getPaddingBottomWithForeground();
        // 6. 遍历子view
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            // 7. 只操作没有GONE的view
            if (child.getVisibility() != GONE) { 
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();

                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;
                // 8. 判断 FrameLayout横向的gravity属性
                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                    case Gravity.CENTER_HORIZONTAL: // 水平居中
                        childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
                        lp.leftMargin - lp.rightMargin;
                        break;
                    case Gravity.RIGHT: // 水平居右
                        if (!forceLeftGravity) {
                            childLeft = parentRight - width - lp.rightMargin;
                            break;
                        }
                    case Gravity.LEFT: // 水平居左,默认情况!
                    default:
                        childLeft = parentLeft + lp.leftMargin;
                }
                // 9. 判断 FrameLayout竖向的gravity属性
                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测量过程
                child.layout(childLeft, childTop, childLeft + width, childTop + height);
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

注释已经写得很详细了,就不在过多介绍!

child.layout() 是针对一个个子view进行测量!

  • 如果 child是一个viewGroup,则会调用ViewGroup中的layout()函数,然后调用具体ViewGroup子类的onLayout()函数!

  • 如果child是一个View,则会调用View中的layout()函数,然后调用具体View子类的onLayout()函数!

父容器在layout()方法中完成了自己的布局之后,就通过onLayout()方法去调用子view的layout()方法,然后子view通过自己的layout()函数来完成自己的布局,这样一层层往下传递就完成了这个View流程树的layout过程!

这里写图片描述

http://blog.csdn.net/crazy1235/article/details/72633389

猜你喜欢

转载自blog.csdn.net/cch___/article/details/79019650