Android onMeasure方法

来源:互联网 发布:九宫图算法口诀 编辑:程序博客网 时间:2024/06/06 03:36

Android onMeasure方法

1、View.onMeasure方法

View中的onMeasure方法调用setMeasuredDimension方法来设置实际的宽和高,使用getDefaultSize方法获取默认的宽高。
在自定义控件中也可以使用setMeasuredDimension来设置宽高。
(1) onMeasure方法

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}
(2) getDefaultSize方法
在getDefaultSize方法中,使用MeasureSpec来获取mode和size,并返回计算后的值。
当mode为UNSPECIFIED时,返回默认值,否则返回建议值。

public static int getDefaultSize(int size, int measureSpec) {    int result = size;    int specMode = MeasureSpec.getMode(measureSpec);    int specSize = MeasureSpec.getSize(measureSpec);    switch (specMode) {    case MeasureSpec.UNSPECIFIED:        result = size;        break;    case MeasureSpec.AT_MOST:    case MeasureSpec.EXACTLY:        result = specSize;        break;    }    return result;}
(3) MeasureSpec
MeasureSpec有三种类型,UNSPECIFIED、EXACTLY和AT_MOST。
UNSPECIFIED表示未定义,即父控件未做限制,可以为任何值,一般设置为0。
EXACTLY表示实际值,即父容器已经指定了具体的值。
AT_MOST表示父容器提供了最大值,但子控件可以选择自己的范围。
使用静态方法来获取实际的mode和size

public static int getMode(int measureSpec)public static int getSize(int measureSpec)
(4) 自定义控件CMeasureView,获取实际的size和mode值。
public class CMeasureView extends View {private final static String LOGTAG = "CMeasureView";public CMeasureView(Context context) {super(context);}public CMeasureView(Context context, AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int mode = MeasureSpec.getMode(widthMeasureSpec);int size = MeasureSpec.getSize(widthMeasureSpec);LogUtil.log(LOGTAG, "width = " + size + " mode = " + getMode(mode));mode = MeasureSpec.getMode(heightMeasureSpec);size = MeasureSpec.getSize(heightMeasureSpec);LogUtil.log(LOGTAG, "height = " + size + " mode = " + getMode(mode));}private String getMode(int mode) {if (mode == MeasureSpec.UNSPECIFIED) {return "UNSPECIFIED";} else if (mode == MeasureSpec.AT_MOST) {return "AT_MOST";} else if (mode == MeasureSpec.EXACTLY) {return "EXACTLY";} else {return "unknow mode";}}}
(5) 布局文件activity_measure.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical" >    <com.blog.demo.control.CMeasureView    android:layout_width="match_parent"    android:layout_height="@dimen/margin_xdpi_30" />    <com.blog.demo.control.CMeasureView    android:layout_width="wrap_content"    android:layout_height="wrap_content" /></LinearLayout>
我们可以看到,当宽高指定大小或者为match_parent时,mode为EXACTLY。当宽高为wrap_parent时,mode为AT_MOST。


2、ViewGroup.onMeasure的用法

ViewGroup除了需要计算自身的宽高以外,还要计算子控件的宽高,系统提供了measureChildren、measureChild和getChildMeasureSpec来支持一般的操作。
(1) measureChildren方法
只要child的Visibility不是GONE,就计算child的宽高,调用measureChild方法。

protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {    final int size = mChildrenCount;    final View[] children = mChildren;    for (int i = 0; i < size; ++i) {        final View child = children[i];        if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {            measureChild(child, widthMeasureSpec, heightMeasureSpec);        }    }}
(2) measureChild方法
使用getChildMeasureSpec方法获得宽高,最后调用View.measure方法设置。

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);}
(3) getChildMeasureSpec方法
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;    }    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);}

3、自定义CMeasureViewGroup

(1) 自定义onMeasure方法
调用getMeasureSize方法获取size,并设置控件的宽高,并且记得调用measureChildren方法来计算子控件的宽高,否则子控件无法显示。同时需要实现onLayout方法,用来计算子控件的位置。

public class CMeasureViewGroup extends ViewGroup {private final static String LOGTAG = "CMeasureViewGroup";public CMeasureViewGroup(Context context) {super(context);}public CMeasureViewGroup(Context context, AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int width = getMeasureSize(widthMeasureSpec);int height = getMeasureSize(heightMeasureSpec);setMeasuredDimension(width, height);measureChildren(widthMeasureSpec, heightMeasureSpec);}private int getMeasureSize(int measureSpec) {int mode = MeasureSpec.getMode(measureSpec);int size = MeasureSpec.getSize(measureSpec);LogUtil.log(LOGTAG, "size = " + size + " mode = " + getMode(mode));if (mode == MeasureSpec.UNSPECIFIED) {return 0;} else if (mode == MeasureSpec.AT_MOST ||mode == MeasureSpec.EXACTLY) {return size;} else {return 0;}}private String getMode(int mode) {if (mode == MeasureSpec.UNSPECIFIED) {return "UNSPECIFIED";} else if (mode == MeasureSpec.AT_MOST) {return "AT_MOST";} else if (mode == MeasureSpec.EXACTLY) {return "EXACTLY";} else {return "unknow mode";}}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {LogUtil.log(LOGTAG, "left = " + l + " top = " + t + " right = " + r + " bottom = " + b);int totalHeight = t;int childCount = getChildCount();for (int i = 0; i < childCount; i++) {View child = getChildAt(i);int measureWidth = child.getMeasuredWidth();int measureHeight = child.getMeasuredHeight();child.layout(l, totalHeight, l + measureWidth, totalHeight + measureHeight);totalHeight += measureHeight;}}}
(2) 布局文件activity_measure_viewgroup.xml
<?xml version="1.0" encoding="utf-8"?><com.blog.demo.control.CMeasureViewGroup xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical" >    <com.blog.demo.control.CMeasureView    android:layout_width="@dimen/margin_xdpi_30"    android:layout_height="@dimen/margin_xdpi_30"    android:background="#ffff0000" /><com.blog.demo.control.CMeasureView    android:layout_width="match_parent"    android:layout_height="@dimen/margin_xdpi_30"    android:background="#ff0000ff" />    <com.blog.demo.control.CMeasureView    android:layout_width="wrap_content"    android:layout_height="@dimen/margin_xdpi_30"    android:background="#ff00ff00" /></com.blog.demo.control.CMeasureViewGroup>

在系统日志中可以看到CMeasureViewGroup和CMeasureView的实际宽高。


(3) 显示结果如下


0 0
原创粉丝点击