Android GUI之View布局,androidguiview

來源:互聯網
上載者:User

Android GUI之View布局,androidguiview

  在清楚了View繪製機制中的第一步測量之後,我們繼續來瞭解分析View繪製的第二個過程,那就是布局定位。繼續跟蹤分析源碼,根據之前的流程分析我們知道View的繪製是從RootViewImpl的performTraversals方法開始的,在此方法中依次調用了performMeasure、performLayout、performDraw等方法進行測量、布局、繪製,那麼下面我們就看看則方performLayout中都做了哪些事情,該方法的關鍵源碼如下:

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,            int desiredWindowHeight) {        mLayoutRequested = false;        mScrollMayChange = true;        mInLayout = true;        final View host = mView;          ……        try {            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());……        } finally {            Trace.traceEnd(Trace.TRACE_TAG_VIEW);        }        mInLayout = false;    }

  從中看出,最關鍵的代碼就是調用了host.layout方法,那麼大家還記不記得host是個什麼東東呢?對了,正是我們之前說的根視圖DecorView。那麼我們就回到DecorView看看它在layout方法中到底做了什麼事情。令人失望的是,我們在DecorView中並沒有發現該方法,不要急,根據該類的繼承體系,我們最終追蹤到layout方法在View中。

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);            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;    }protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}

  該方法中的4個參數代表了當前的View與父View之間4個方向上的距離,同時從說明中可以看出,此方法不應該被子類重寫,如果需要重新布局,可以在子類中重寫的方法是onLayout,此方法在View中是個空方法,什麼都沒有寫。可實際上layout的方法在View中並沒有被標識為final,這就意味是可以被重寫的。

  繼續查看ViewGoup中的相關代碼,果然layout被重寫了並添加了final標識,同時onLayout被標識為抽象方法,所以繼承了ViewGroup的類是,是不能重寫layout方法的,並且要實現onLayout方法。從代碼可以看出,雖然ViewGroup重寫了layout,實際本質上還是調用了View的layout,然後通過調用onLayout方法最終完成布局定位的。

       @Override    public final void layout(int l, int t, int r, int b) {        if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {            if (mTransition != null) {                mTransition.layoutChange(this);            }            super.layout(l, t, r, b);        } else {            // record the fact that we noop'd it; request layout when transition finishes            mLayoutCalledWhileSuppressed = true;        }    }    @Override    protected abstract void onLayout(boolean changed,            int l, int t, int r, int b);

  在DecorView中,並沒有發現onLayout方法,所以它使用的肯定是其父類FrameLayout中的,找到FrameLayout的源碼,可以查看到onLayout方法,具體如下:

  @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        layoutChildren(left, top, right, bottom, false /* no force left gravity */);    }    void layoutChildren(int left, int top, int right, int bottom,                                  boolean forceLeftGravity) {        final int count = getChildCount();        final int parentLeft = getPaddingLeftWithForeground();        final int parentRight = right - left - getPaddingRightWithForeground();        final int parentTop = getPaddingTopWithForeground();        final int parentBottom = bottom - top - getPaddingBottomWithForeground();        mForegroundBoundsChanged = true;                for (int i = 0; i < count; i++) {            final View child = getChildAt(i);            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;                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;                }                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;                }                child.layout(childLeft, childTop, childLeft + width, childTop + height);            }        }    }

  從方法中,我們可以看出在Framelayout中最終調用了layoutChildren方法,在該方法中根據測量結果和一些布局屬性對容器中每一個View都調用了layout方法進行了布局。根據以上的程式碼分析,我們可以得出View布局定位的流程圖如下。

 

  疑問諮詢或技術交流,請加入官方QQ群: (452379712)

 

作者:傑瑞教育
出處:http://www.cnblogs.com/jerehedu/ 
本文著作權歸煙台傑瑞教育科技有限公司和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文串連,否則保留追究法律責任的權利。 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.