Android View

来源:互联网 发布:策略游戏 知乎 编辑:程序博客网 时间:2024/06/17 05:29

Layout起布局作用,确定View的位置。当ViewGroup确定位置后,会遍历子View并调用其layout方法确定子View的位置。

View的onLayout方法

View的onLayout方法是一个空实现,因为子View不包含其他View,不需要被调用。

ViewGroup的onLayout方法

ViewGroup会先调用layout方法:

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

ViewGroup的onLayout方法是一个抽象方法,由子类去实现,主要是让ViewGroup在这个方法确定每个子View的位置。

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

我们需要关心的其实就是这个方法,以LinearLayout为例:

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

根据android:orientation属性给子View设置不同的位置,看layoutVertical方法代码:

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

在layout时,会用到子View测量后的值,所以先测量,后布局。最后调用setChildFrame方法:

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

调用子View的layout方法确定位置。

我们自定义流式布局时,就需要像这样重写onLayout方法。