轉載請說明出處:http://blog.csdn.net/ff20081528/article/details/17784911
剛過完本身的本命年,新的一年但願本身有個新的開始,祝本身在新的一年裏一路順風,同時也祝廣大的朋友們新年新氣象,收穫多多!
1、android中view的layout過程總概
Layout過程其實就是父視圖按照子視圖的大小及佈局參數將子視圖放在窗口的合適的位置上。
視圖的佈局過程是從ViewRoot對象調調用根視圖的layout()方法開始,接着layout()方法調用根視圖的onLayout()方法,onLayout()方法會對所包含的子視圖逐一執行layout操做,若是子視圖是ViewGroup子類對象,則繼續調用子視圖的layout(),重複這一過程。若是子視圖是View子類對象,則在子視圖重載的onLayout()內部只須要將本身佈局到視圖中,不須要對子視圖進行layout操做,這樣一次layout過程結束。過程以下圖:
2、layout詳細過程
View中的layout()方法源碼(ViewGroup類繼承了View類,layout過程先從ViewGroup子類開始):
- /**
- * Assign a size and position to a view and all of its
- * descendants
- *
- * <p>This is the second phase of the layout mechanism.
- * (The first is measuring). In this phase, each parent calls
- * layout on all of its children to position them.
- * This is typically done using the child measurements
- * that were stored in the measure pass().
- *
- * Derived classes with children should override
- * onLayout. In that method, they should
- * call layout on each of their their children.
- *
- * @param l Left position, relative to parent
- * @param t Top position, relative to parent
- * @param r Right position, relative to parent
- * @param b Bottom position, relative to parent
- */
- public final void layout(int l, int t, int r, int b) {
- boolean changed = setFrame(l, t, r, b);
- if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
- if (ViewDebug.TRACE_HIERARCHY) {
- ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
- }
-
- onLayout(changed, l, t, r, b);
- mPrivateFlags &= ~LAYOUT_REQUIRED;
- }
- mPrivateFlags &= ~FORCE_LAYOUT;
- }
a) 首先咱們看這個方法的定義,用了關鍵字final,說明此方法是不可被重寫的,這樣也就保證了View的layout過程是不變的。四個參數看註釋,左、上、右、下分別相距父視圖的距離。java
b) 調用setFrame(l,t,r,b)將位置保存起來,這些參數將保存到View內部變量 (mLeft、mTop、mRight、mBottom)中。保存完變量前,會先對比這些參數是否和原來的相同,若是相同,則什麼都不作,若是不一樣則進行從新賦值,並在賦值前給mPrivateFlags中添加DRAWN標識,同時調用invalidate()通知View系統原來佔用的位置須要重繪。android
c) 調用onLayout(),View中定義的onLayout()方法默認什麼都不作,View系統提供onLayout()方法的目的是爲了使系統包含的子視圖的父視圖可以在onLayout()方法對子視圖進行位置分配,正由於如此,若是是父視圖,則必須重寫onLayout(),也正由於如此ViewGroup類纔會把onLayout重載改爲了abstract類型。ide
d)清除mPrivateFlags中的LAYOUT_REQUIRED標識,由於layout操做已經完成。佈局
上面提到的setFrame方法源碼以下:this
- protected boolean setFrame(int left, int top, int right, int bottom) {
- boolean changed = false;
- if (DBG) {
- Log.d("View", this + " View.setFrame(" + left + "," + top + ","
- + right + "," + bottom + ")");
- }
- if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
- changed = true;
- // Remember our drawn bit
- int drawn = mPrivateFlags & DRAWN;
- // Invalidate our old position
- invalidate();
- int oldWidth = mRight - mLeft;
- int oldHeight = mBottom - mTop;
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
- mPrivateFlags |= HAS_BOUNDS;
- int newWidth = right - left;
- int newHeight = bottom - top;
- if (newWidth != oldWidth || newHeight != oldHeight) {
- onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
- }
- if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
- // If we are visible, force the DRAWN bit to on so that
- // this invalidate will go through (at least to our parent).
- // This is because someone may have invalidated this view
- // before this call to setFrame came in, therby clearing
- // the DRAWN bit.
- mPrivateFlags |= DRAWN;
- invalidate();
- }
- // Reset drawn bit to original value (invalidate turns it off)
- mPrivateFlags |= drawn;
- mBackgroundSizeChanged = true;
- }
- return changed;
- }
View中的onLayout()方法以下:
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- }
而ViewGroup中的onLayout()方法以下:spa
- @Override
- protected abstract void onLayout(boolean changed,
- int l, int t, int r, int b);
轉載請說明出處:http://blog.csdn.net/ff20081528/article/details/17784911 .net
由於ViewGroup中的onLayout()方法是一個抽象方法,因此下面咱們用他的子類LinearLayout中的onLayout()方法來分析。源碼以下:code
onlayout()方法:對象
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- if (mOrientation == VERTICAL) {
- layoutVertical();
- } else {
- layoutHorizontal();
- }
- }
layoutVertical()方法源碼:blog
- void layoutVertical() {
- final int paddingLeft = mPaddingLeft;
- int childTop = mPaddingTop;
- int childLeft;
- // Where right end of child should go
- final int width = mRight - mLeft;
- int childRight = width - mPaddingRight;
- // Space available for child
- int childSpace = width - paddingLeft - mPaddingRight;
- final int count = getVirtualChildCount();
- final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
- final int minorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
- if (majorGravity != Gravity.TOP) {
- switch (majorGravity) {
- case Gravity.BOTTOM:
- // mTotalLength contains the padding already, we add the top
- // padding to compensate
- childTop = mBottom - mTop + mPaddingTop - mTotalLength;
- break;
- case Gravity.CENTER_VERTICAL:
- childTop += ((mBottom - mTop) - mTotalLength) / 2;
- break;
- }
- }
- 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();
- int gravity = lp.gravity;
- if (gravity < 0) {
- gravity = minorGravity;
- }
- switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- childLeft = paddingLeft + lp.leftMargin;
- break;
- case Gravity.CENTER_HORIZONTAL:
- childLeft = paddingLeft + ((childSpace - childWidth) / 2)
- + lp.leftMargin - lp.rightMargin;
- break;
- case Gravity.RIGHT:
- childLeft = childRight - childWidth - lp.rightMargin;
- break;
- default:
- childLeft = paddingLeft;
- break;
- }
- childTop += lp.topMargin;
- setChildFrame(child, childLeft, childTop + getLocationOffset(child),
- childWidth, childHeight);
- childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
- i += getChildrenSkipCount(child, i);
- }
- }
- }
a) LinearLayout中的子視圖有兩種佈局方式,一個是縱向的,一個是橫向的,這裏咱們以縱向的分析。
b) 得到子視圖的寬度。
c) 根據父視圖中的grarity屬性,來判斷子視圖的起始位置。
d) 開始for()循環,爲每一個子視圖分配位置。對於每一個子視圖首先取出子視圖的LayoutParams屬性,而且得到gravity的值。根據gravity的值肯定水平方向的起始位置,三種值分別爲:LEFT,CENTER_HORIZONTAL和RIGHT.接着調用setChildFrame(),該方法內部實際上就是調用child.layout()爲子視圖設置佈局位置。
轉載請說明出處:http://blog.csdn.net/ff20081528/article/details/17784911