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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章