android 自定义View之View的测量(onMeasure()方法)

       在自定义控件的过程中,系统在绘制View前,必须对View进行测量,已使后面的onLayout(设置View的放置位置)能够顺利进行。而对VIew的测量的过程则是在onMeasure()中进行的。可能这时有的同学就发现问题了,说,自己以前自定义的View没有重写onMeasure()方法,仍然可以正常运行,这是因为什么呢?

       让我们先从头说起,android系统给我们提供了一个设计短小精悍却功能强大的类———MeasureSpec类,通过它来帮助我们测量View;

       测量规格,包含测量要求和尺寸的信息,有三种模式

  • UNSPECIFIED
    父视图不对子视图有任何约束,它可以达到所期望的任意尺寸。比如 ListView、ScrollView,一般自定义 View 中用不到。

  • EXACTLY
    父视图为子视图指定一个确切的尺寸,而且无论子视图期望多大,它都必须在该指定大小的边界内,对应的属性为 match_parent 或具体值,比如 100dp,父控件可以通过MeasureSpec.getSize(measureSpec)直接得到子控件的尺寸。

  • AT_MOST
    父视图为子视图指定一个最大尺寸。子视图必须确保它自己所有子视图可以适应在该尺寸范围内,对应的属性为 wrap_content,这种模式下,父控件无法确定子 View 的尺寸,只能由子控件自己根据需求去计算自己的尺寸,这种模式就是我们自定义视图需要实现测量逻辑的情况。

相信从上面的三种模式就可以解答上面有些同学的疑问了,下面揭晓答案,View类的onMeasure()方法只支持EXACTLY模式,所以如果在自定义控件的时候不重写onMeasure()方法的话,就只能使用EXACTLY模式。控件可以响应你指定的具体的宽高值或者是match_parent属性,(这就像是上面的同学说的那样,没有重写onMeasure()方法),而如果要让自定义View支持wrap_content属性,那么就必须需要重写onMeasure()方法来指定wrap_content时的大小。

      下面我们来看一个简单的实例,演示如何进行View的测量,首先要重写onMeasure()方法,该方法如下所示。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
}
在IDE中按住Ctrl键查看super。onMeasure()方法。可以发现,系统最终调用setMeasuredDimension(int measuredWidth,int measuredHeight)方法将测量后的宽高值设置进去,从而完成测量工作,所以在重写onMeasure()方法后,最终要做的工作是把测量后的宽高值作为参数设置给setMeasuredDimension()方法。

代码如下:

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

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

以measureWidth()为例,第一步,从MeasureSpc对象中提取出具体的测量模式和大小

int specMode=MeasureSpec.getMode(widthMeasureSpec);
int specSize=MeasureSpec.getSize(widthMeasureSpec);

接下来通过判断测量的模式,给出不同的测量值。当specMode为EXACTLY时,直接使用指定的specSize即可;当specMode为其他两个模式时,需要给它一个默认的大小。特别的,如果指定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 {
        //这样,当时用wrap_content时,View就获得一个默认值200px,而不是填充整个父布局。
        result = 200;
        if (specMode == MeasureSpec.AT_MOST) {
            result = Math.min(result, specSize);
        }
    }

    return result;
}

好了,今天的View的测量就讲到这里,后面有时间会讲一下View的绘制流程。




     


  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值