上一篇说了View测量逻辑调用,以及父视图如何影响到View的测量等概念,这一篇测量在代码中是怎么具体实现的。
首先说ViewGroup类吧,因为该类是所有容器类的父类,所谓容器类就是布局类,比如LinearLayout、TableLayout等,用来容纳VIew控件的。
ViewGroup提供了三个类似的方法用于对子视图进行measure()操作:
①measureChildren():正如其名,Children是复数啊,该方法内部使用for循环调用measureChild()方法对每一个子视图进行measure操作,源码如下:
<span style="font-size:18px;"> protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { final int size = mChildrenCount; //所有的子视图,至于xml文件如何转换为View对象的已经介绍过了 final View[] children = mChildren; //采用for循环遍历 for (int i = 0; i < size; ++i) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { //如果可见的话才对子视图进行测量 measureChild(child, widthMeasureSpec, heightMeasureSpec); } } }</span>② measureChild():为指定的子视图进行measure操作,measureChildren()方法会调用此方法,源码如下:
<span style="font-size:18px;"> protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { //首先获取其布局参数,前面的关于xml文件转换为View对象的文章中已经说过, //在转换过程中已经为LayoutParams赋值了, final LayoutParams lp = child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); //开始调用measure函数进行测量 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }</span>我记得好像说过LayoutParams类是个什么情况 - -没有详细拿出来,LayoutParams类相当重要啊,每一个容器类都会有一个LayoutParams的内部类,一个视图会使用LayoutParams类来封装必要信息告诉父容器怎么去为自己布局,ViewGroup的LayoutParams以及MarginLayoutParams类如下:
<span style="font-size:18px;">public static class LayoutParams { /** * Special value for the height or width requested by a View. * FILL_PARENT means that the view wants to be as big as its parent, * minus the parent's padding, if any. This value is deprecated * starting in API Level 8 and replaced by {@link #MATCH_PARENT}. */ //这个值熟悉吗,这是4.1.2版本了,此属性已经不被推荐使用 public static final int FILL_PARENT = -1; //熟悉吗 public static final int MATCH_PARENT = -1; public static final int WRAP_CONTENT = -2; public int width; public int height; public LayoutAnimationController.AnimationParameters layoutAnimationParameters; public LayoutParams(Context c, AttributeSet attrs) { TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout); setBaseAttributes(a, R.styleable.ViewGroup_Layout_layout_width, R.styleable.ViewGroup_Layout_layout_height); a.recycle(); } public LayoutParams(int width, int height) { this.width = width; this.height = height; } /** * @param source The layout params to copy from. */ public LayoutParams(LayoutParams source) { this.width = source.width; this.height = source.height; } /** * Used internally by MarginLayoutParams. * @hide */ LayoutParams() { } protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { width = a.getLayoutDimension(widthAttr, "layout_width"); height = a.getLayoutDimension(heightAttr, "layout_height"); } /** * @hide */ public void onResolveLayoutDirection(int layoutDirection) { } /** * @hide */ public String debug(String output) { return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) + ", height=" + sizeToString(height) + " }"; } public void onDebugDraw(View view, Canvas canvas) { } protected static String sizeToString(int size) { if (size == WRAP_CONTENT) { return "wrap-content"; } if (size == MATCH_PARENT) { return "match-parent"; } return String.valueOf(size); } } /** * Per-child layout information for layouts that support margins. * See *继承了本类的LayoutParams */ public static class MarginLayoutParams extends ViewGroup.LayoutParams { public int leftMargin; public int topMargin; public int rightMargin; public int bottomMargin; /** * @hide */ @ViewDebug.ExportedProperty(category = "layout") public int startMargin = DEFAULT_RELATIVE; /** * @hide */ @ViewDebug.ExportedProperty(category = "layout") public int endMargin = DEFAULT_RELATIVE; static private final int DEFAULT_RELATIVE = Integer.MIN_VALUE; public MarginLayoutParams(Context c, AttributeSet attrs) { super(); TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout); setBaseAttributes(a, R.styleable.ViewGroup_MarginLayout_layout_width, R.styleable.ViewGroup_MarginLayout_layout_height); int margin = a.getDimensionPixelSize( com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1); if (margin >= 0) { leftMargin = margin; topMargin = margin; rightMargin= margin; bottomMargin = margin; } else { leftMargin = a.getDimensionPixelSize( R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0); topMargin = a.getDimensionPixelSize( R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0); rightMargin = a.getDimensionPixelSize( R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0); bottomMargin = a.getDimensionPixelSize( R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0); startMargin = a.getDimensionPixelSize( R.styleable.ViewGroup_MarginLayout_layout_marginStart, DEFAULT_RELATIVE); endMargin = a.getDimensionPixelSize( R.styleable.ViewGroup_MarginLayout_layout_marginEnd, DEFAULT_RELATIVE); } a.recycle(); } public MarginLayoutParams(int width, int height) { super(width, height); } /** * Copy constructor. Clones the width, height and margin values of the source. * @param source The layout params to copy from. */ public MarginLayoutParams(MarginLayoutParams source) { this.width = source.width; this.height = source.height; this.leftMargin = source.leftMargin; this.topMargin = source.topMargin; this.rightMargin = source.rightMargin; this.bottomMargin = source.bottomMargin; this.startMargin = source.startMargin; this.endMargin = source.endMargin; } /** * {@inheritDoc} */ public MarginLayoutParams(LayoutParams source) { super(source); } public void setMargins(int left, int top, int right, int bottom) { leftMargin = left; topMargin = top; rightMargin = right; bottomMargin = bottom; } /** * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()} * @hide */ public void setMarginsRelative(int start, int top, int end, int bottom) { startMargin = start; topMargin = top; endMargin = end; bottomMargin = bottom; } /** * Returns the start margin in pixels * @hide */ public int getMarginStart() { return startMargin; } /** * @hide */ public int getMarginEnd() { return endMargin; } /** * @hide */ public boolean isMarginRelative() { return (startMargin != DEFAULT_RELATIVE) || (endMargin != DEFAULT_RELATIVE); } /** * @hide */ @Override public void onResolveLayoutDirection(int layoutDirection) { switch(layoutDirection) { case View.LAYOUT_DIRECTION_RTL: leftMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : leftMargin; rightMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : rightMargin; break; case View.LAYOUT_DIRECTION_LTR: default: leftMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : leftMargin; rightMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : rightMargin; break; } } /** * @hide */ @Override public void onDebugDraw(View view, Canvas canvas) { drawRect(canvas, view.getLeft() - leftMargin, view.getTop() - topMargin, view.getRight() + rightMargin, view.getBottom() + bottomMargin, Color.MAGENTA); } }</span>
<span style="font-size:14px;"> protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } </span>