您的位置:首页 > 移动开发 > Android开发

android 自定义view-onMeasure

2016-01-21 11:11 561 查看
当进行自定义view时我们首先需要知道这个view的大小,在android中是通过onMeasure来进行测量的,在看《android群英传》后记录学习过程。

在自定义view时我们需要重写onMeasure方法进行view的测量。

/**
* <p>
* Measure the view and its content to determine the measured width and the
* measured height. This method is invoked by {@link #measure(int, int)} and
* should be overriden by subclasses to provide accurate and efficient
* measurement of their contents.
* </p>
*
* <p>
* <strong>CONTRACT:</strong> When overriding this method, you
* <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the
* measured width and height of this view. Failure to do so will trigger an
* <code>IllegalStateException</code>, thrown by
* {@link #measure(int, int)}. Calling the superclass'
* {@link #onMeasure(int, int)} is a valid use.
* </p>
*
* <p>
* The base class implementation of measure defaults to the background size,
* unless a larger size is allowed by the MeasureSpec. Subclasses should
* override {@link #onMeasure(int, int)} to provide better measurements of
* their content.
* </p>
*
* <p>
* If this method is overridden, it is the subclass's responsibility to make
* sure the measured height and width are at least the view's minimum height
* and width ({@link #getSuggestedMinimumHeight()} and
* {@link #getSuggestedMinimumWidth()}).
* </p>
*
* @param widthMeasureSpec horizontal space requirements as imposed by the parent.
*                         The requirements are encoded with
*                         {@link android.view.View.MeasureSpec}.
* @param heightMeasureSpec vertical space requirements as imposed by the parent.
*                         The requirements are encoded with
*                         {@link android.view.View.MeasureSpec}.
*
* @see #getMeasuredWidth()
* @see #getMeasuredHeight()
* @see #setMeasuredDimension(int, int)
* @see #getSuggestedMinimumHeight()
* @see #getSuggestedMinimumWidth()
* @see android.view.View.MeasureSpec#getMode(int)
* @see android.view.View.MeasureSpec#getSize(int)
*/
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
以上是父类view的onMeasure方法实现,我们可以看到系统会最终调用setMeasureDimension(width,height)方法将测量后的宽和高设置进去,完成view的测量,所以我们重写view的时候需要把测量的view的宽和高传到setMeasureDimension里面去完成测量。

如何测量:

android中提供给我们一个MeasureSpec类来完成view的测量。MeasureSpec是一个32位的int 值,其中高2位为测量的模式,低2位为测量的大小。测量的模式主要分为EXACTLY、AT_MOST、UNSPECIFIED三种值。

EXACTLY
准确精度值模式,当设置控件的宽和高为具体的值是如:100dp等就是用的这个值,或者match_parent

AT_MOST
当设置控件的宽和高为wrap_content时,则其大小为控件的内容变化而变化。

UNSPECIFIED

不指定测量大小模式,view想多大就有多大。

view的默认的模式就是第一种精确模式,所以自定义view时不重写onMeasure方法就是使用EXACTLY模式,即支持view的具体值或者match_parent属性,而如果view的宽和高指定为wrap_content时就必须重写onMeasure方法。

下面通过一个具体的实例进行加深理解:
public class TestCustomeView2 extends View {
public TestCustomeView2(Context context) {
super(context);
}

public TestCustomeView2(Context context, AttributeSet attrs) {
super(context, attrs);
}

public TestCustomeView2(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}

}
布局文件activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".act.MainActivity">

<com.customview.widget.TestCustomeView2
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#fffaaa"/>
</RelativeLayout>


我们自定义view,没有重写onMeasure方法,此时布局文件写的宽和高为wrap_content,此时系统不知道该绘制多大的,所致默认会填充整个父布局,我们重写onMeasure方法就是让系统能够知道view的大小从而能够绘制出来。



当我们指定了控件的具体大小或者为match_parent时系统此刻就知道该绘制多大的了。
布局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".act.MainActivity">

<com.customview.widget.TestCustomeView2
android:layout_width="300dp"
android:layout_height="200dp"
android:background="#fffaaa"/>
</RelativeLayout>



重写onMeasure方法获取view的大小:
public class TestCustomeView extends View {
public TestCustomeView(Context context) {
super(context);
}

public TestCustomeView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public TestCustomeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
}

/**
* 获取view的高度,当没有指定大小时给予默认的高为400px
* @param measureSpec
* @return
*/
private int measureHeight(int measureSpec) {
int result = 0;

int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
//如果布局控件指定大小则返回其大小值,否则返回默认的值
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = 400;
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;

}

/**
* 获取view的宽度,当没有指定大小时给予默认的高为200px
* @param measureSpec
* @return
*/
private int measureWidth(int measureSpec) {
int result = 0;

int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
//如果布局控件指定大小则返回其大小值,否则返回默认的值
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = 200;
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
}
布局activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".act.MainActivity">

<com.customview.widget.TestCustomeView
android:layout_width="400dp"
android:layout_height="200dp"
android:background="#fffaaa"/>
</RelativeLayout>



若我们没有指定其大小,而是使用wrap_content时则会显示默认的大小200px和400dpx
布局activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".act.MainActivity">

<com.customview.widget.TestCustomeView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#fffaaa"/>
</RelativeLayout>



通过以上实例我们就可以加深对onMeasure方法的理解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: