笔记 Androd 自定义控件学习(一)

View的测量

Android 系统在绘制View前,需要对View进行测量,即告诉系统该画一个多大的View,这个过程在 onMeasure() 方法中进行。Android系统给我们提供了一个帮助我们测量View的类—-MeasureSpec类,MeasureSpec是一个32为的值,其中高2位为测量的模式,低30为为测量的大小。

测量的模式有三种:

  • EXACTLY
    精确模式,当我们将控件的layout_width 属性或layout_height属性指定为具体数值时,比如:android:layout_width = “100dp” ,或者指定为 match_parent 属性时(占据父View的大小),系统使用的是 EXACTLY 模式。

  • AT_MOST
    最大模式,当我们将控件的layout_width 属性或layout_height属性指定为 wrap_content 时,控件大小一般随着控件的子控件或内容的变化而变化,此时控件的尺寸只要不超过父控件永许的最大尺寸即可。

  • UNSPECIFIED
    不指定其大小测量模式,View想多大就多大,通常情况下在绘制自定义View时才会使用。

View类默认的onMeasure()方法只支持EXACTLY模式,所以如果在自定义控件的时候不重写onMeasure()方法的话,就只能使用EXACTLY模式。控件可以响应指定的具体宽高值或者是match_parent 属性,而如果要让自定义View支持 wrap_content 属性,那么就必须重写 onMeasure() 方法来指定 wrap_content 时的大小。

通过MeasureSpec这一个类,我们就获取了View的测量模式和View想要绘制的大小,有了这些信息,我们就可以控制View最后显示的大小。
接下来我们看一个简单的实例,演示如何进行View的测试量,首先我们重写onMeasure()方法。

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

通过查看super.onMeasure()方法,可以发现,系统最终会调用 setMeasuredDimension(int measuredWidth, int measuredHeight) 方法将测量后的宽高值设置进去,从而完成测量工作。所以在重写 onMeasure() 方法后,最终要做的工作就是把测量后的宽高值作为参数设置给 setMeasuredDimension()方法。
所以重写的 onMeasure()方法代码如下所示:

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

在onMeasure()方法中,我们调用自定义的measureWidth()方法和measureheight()方法,分别对宽高进行重新定义,参数则是宽和高的MeasureSpec 对象,MeasureSpec 对象中包含了测量的模式和测量的大小。

下面我们就来看一下如何自定义测量值:
1. 从MeasureSpec 对象中提取出具体的测量模式和大小

int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
  1. 通过判断测量的模式,给出不同的测量值,当specMode为 EXACTLY 时,直接使用指定的 specSize 即可,当specMode为AT_MOST或UNSPECIFIED时,需要给它一个默认的大小(特别的,如果指定wrap_content属性,即AT_MOST模式,则需要给它一个默认的大小与specSize中最小的一个来作为最后的测量值)。measureWidth方法如下:
    private int measureWidth(int widthMeasureSpec){
        int result = 0;
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int specSize = MeasureSpec.getSize(widthMeasureSpec);

        if(specMode == MeasureSpec.EXACTLY){
            result = specSize;
        }else{
            result = 300;
            if (specMode == MeasureSpec.AT_MOST){
                result = Math.min(result,specSize);
            }
        }
        return result;
    }

    private int measureheight(int heightMeasureSpec){
        int result = 0;
        int specMode = MeasureSpec.getMode(heightMeasureSpec);
        int specSize = MeasureSpec.getSize(heightMeasureSpec);

        if(specMode == MeasureSpec.EXACTLY){
            result = specSize;
        }else{
            result = 300;
            if (specMode == MeasureSpec.AT_MOST){
                result = Math.min(result,specSize);
            }
        }
        return result;
    }

通过以上两个方法,我们就完成了对宽和高的自定义。
在布局文件中,指定确定的宽高值为400px,程序运行效果如图1所示
当指定宽高属性为 match_patent 属性时,程序运行效果如图2所示
当指定宽高属性为 wrap_content 属性时,如果不重写 onMeasure()方法,那么系统就不知道该默认多大的尺寸,因此,他就会默认填充整个父布局,所以重写onMeasure()方法的目的,就是为了能够给View一个wrap_content属性下的默认大小,程序运行效果如图2所示

图1:
这里写图片描述

图2:
这里写图片描述

图3
这里写图片描述

可以发现,当指定wrap_content属性时,View就获得了额一个默认值 200px,而不再是填充父布局。
以上就是View大小的测量的相关内容。

以上内容来之Android 群英传,感谢前辈们的付出。

猜你喜欢

转载自blog.csdn.net/zhu522959034/article/details/80675676