上一篇说了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>
③measureChildWithMargins():该函数与measureChild的唯一区别就是measure的时候会考虑把margin 以及padding作为子视图的一部分。源码如下!:
<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>