Android 自定义View onMeasure 的理解

Android 自定义View onMeasure 的理解

Android开发中如果有限的控件满足不了需求,可能会用到自定义View,一般情况下,自定义View都需要继承View类的onMeasure方法,onMeasure方法是用来计算View的大小的,下面我们就具体探究这些问题。

首先,我们写一个自定义View,直接调用系统默认的onMeasure函数,看看会是怎样的现象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package cn.addapp.customview;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
public class CustomView extends View {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
  1. 父控件使用match_parent,CustomView使用match_parent

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <LinearLayout 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"
    android:orientation="vertical">
    <cn.addapp.customview.CustomView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="10dp"
    android:background="@android:color/black"/>
    </LinearLayout>

    这里加了10dp的margin并且把View的背景设置为了黑色,是为了方便辨别我们的CustomView,效果如下:

    magin 10dp

    我们可以看到,默认情况下,如果父控件和CustomView都使用match_parent,则CustomView会充满父控件。

  2. 父控件使用match_parent,CustomView使用wrap_content

    把layout文件中,CustomView的layout_width/layout_height替换为wrap_content,你会发现,结果依然是充满父控件。

  3. 父控件使用match_parent,CustomView使用固定的值

    把layout文件中,CustomView的layout_width/layout_height替换为50dp,你会发现,CustomView的显示结果为50dpx50dp,如图所示:

  4. 父控件使用固定的值,CustomView使用match_parent或者wrap_content

    那么,如果把父控件的layout_width/layout_height替换为50dp,CustomView设置为match_parent或者wrap_content,你会发现,CustomView的显示结果也是为50dpx50 dp。

  5. 结论

    如果自定义的CustomView采用默认的onMeasure函数,行为如下:

    (1) CustomView设置为 match_parent 或者 wrap_content 没有任何区别,其显示大小由父控件决定,它会填充满整个父控件的空间。

    (2) CustomView设置为固定的值,则其显示大小为该设定的值。

    如果你的自定义控件的大小计算就是跟系统默认的行为一致的话,那么你就不需要重写onMeasure函数了。

  6. 编写onMeasure函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    package cn.addapp.customview;
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.View;
    public class CustomView extends View {
    private static final int DEFAULT_VIEW_WIDTH = 100;
    private static final int DEFAULT_VIEW_HEIGHT = 100;
    public CustomView(Context context) {
    super(context);
    }
    public CustomView(Context context, AttributeSet attrs) {
    super(context, attrs);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthSize = measureSize(DEFAULT_VIEW_WIDTH, widthMeasureSpec);
    int heightSize = measureSize(DEFAULT_VIEW_HEIGHT, heightMeasureSpec);
    setMeasuredDimension(widthSize, heightSize);
    }
    protected int measureSize( int defaultSize, int measureSpec ) {
    int result = defaultSize;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);
    //1. layout给出了确定的值,比如:100dp
    //2. layout使用的是match_parent,但父控件的size已经可以确定了,比如设置的是具体的值或者 match_parent
    if (specMode == MeasureSpec.EXACTLY) {
    result = specSize; //建议:result直接使用确定值
    }else {
    //UNSPECIFIED,没有任何限制,所以可以设置任何大小
    result = defaultSize; //期望当前自定义控件自行决定大小
    //1. layout使用的是wrap_content
    //2. layout使用的是match_parent,但父控件使用的是确定的值或者wrap_content
    if (specMode == MeasureSpec.AT_MOST){
    result = Math.min(defaultSize, specSize); //建议:result不能大于specSize
    }
    }
    return result;
    }
    }

    具体看规则图1和图2:

    规则图1

    规则图2

    这样重载了onMeasure函数之后,你会发现,当CustomView使用match_parent的时候,它会占满整个父控件,而当CustomView使用wrap_content的时候,它的大小则是代码中定义的默认大小100x100像素。当然,你也可以根据自己的需求改写measureDimension()的实现。

如果文章对您有用,请随意打赏,谢谢。