您的位置:首页 > 其它

View onMeasure测量过程解析

2017-11-26 13:24 337 查看
参考文章:

Android自定义View:MeasureSpec的真正意义与View大小控制

源码解析Android中View的measure量算过程

布局测量过程:

1.generateLayoutParams(AttributeSet attrs)

系统加载xml布局文件时,会调用这个方法生成LayoutParams。

ViewGroup中有三种生成LayoutParams的方法,自定义Layout中如果也自定义了LayoutParams,必须要重写这个三个方法,如果想自定义layout可以参考FrameLayout。

public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}

/**
* Returns a safe set of layout parameters based on the supplied layout params.
* When a ViewGroup is passed a View whose layout params do not pass the test of
* {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
* is invoked. This method should return a new set of layout params suitable for
* this ViewGroup, possibly by copying the appropriate attributes from the
* specified set of layout params.
*
* @param p The layout parameters to convert into a suitable set of layout parameters
*          for this ViewGroup.
*
* @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
*         of its descendants
*/
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return p;
}

/**
* Returns a set of default layout parameters. These parameters are requested
* when the View passed to {@link #addView(View)} has no layout parameters
* already set. If null is returned, an exception is thrown from addView.
*
* @return a set of default layout parameters or null
*/
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}

2.顶层布局通过调用View.measure(int widthMeasureSpec, int heightMeasureSpec),这个方法是final,
在该方法中会调用onMeasure(int widthMeasureSpec, int heightMeasureSpec),这个是实际测量方法,所有子类都必须实现这个方法实现自己的测量方案。

子类在实现该方法中调用measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)
或者measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed),这两个方法实现
原理一样,都是调用View.measure(int widthMeasureSpec, int heightMeasureSpec)实现对childView的测量,这样就实现了嵌套测量。

protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();

final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);

child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

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

**子类是可以重写这两个方法已实现自己的对childView的测量方式,目前只有几个ScrollView重写了两个方法。当然子类也可以不调用这两个方法,自己实现,其实很简单,主要是根据LayoutParams、padding、margin等调用getChildMeasureSpec(int spec, int padding, int childDimension)方法生成childMeasureSpec,然后调用child.measure(childWidthMeasureSpec, childHeightMeasureSpec); **

3.在getChildMeasureSpec(int spec, int padding, int childDimension)方法中可以很清楚的看见父布局是
怎样生成对childView的约束的childWidthMeasureSpec。

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);

int size = Math.max(0, specSize - padding);

int resultSize = 0;
int resultMode = 0;

switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;

// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;

// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
//noinspection ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

4.onMeasure方法最后都需要调用setMeasuredDimension(int measuredWidth, int measuredHeight)方法设置测量结果才能生效,**自定义Layout中在调用该方法之前最好调用resolveSizeAndState方法生成正确的尺寸,**参考FrameLayout的onMeasure方法。

5.MeasureSpec.UNSPECIFIED未对childView做限制,可以随意大小,一般是ScrollView、AdapterView等在重写measureChildWithMargins和measureChild方法中传给childView。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: