彻底理解自定义View的测量

 自定义ViewAndroid开发中是个非常重要的模块,我们一直在使用

android:layout_width="match_parent"
android:layout_height="wrap_content" 

这样的属性设置,但是你知道这些属性是怎么执行的吗?不同的设置是如何产生不同的效果?

下面我从源码分析下自定义View的第一步,View的测量,这是你看到的所有视图都要进行的一步,因为这就像我们现实当中的画图一样,你要先确定纸的大小,然后在画,自定义View看似复杂,整体上就2步,测量和绘制。

这里不按照国际惯例,先上图在分析上代码的过程,个人认为这样不太明白。

我们先看自定义View的代码:

/**
 * 作者:dongcai on 2016/8/18 10:24
 * 邮箱:lidongcaiocc@163.com
 */
public class MyView extends View {
   //构造至少实现一个
    public MyView(Context context) {
        super(context);
    }

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

    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       //设置最终的占位大小
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
    }

     //通过模式设置尺寸
    private int measureHeight(int heightMeasureSpec) {
        int finalSize = 0;
        int mode = MeasureSpec.getMode(heightMeasureSpec);
        int size = MeasureSpec.getSize(heightMeasureSpec);

        if (mode == MeasureSpec.EXACTLY) {
            finalSize = size;
        } else {
            finalSize = 350;
            if (mode == MeasureSpec.AT_MOST) {
                finalSize = Math.min(finalSize, size);
            }
        }

        return finalSize;
    }

      //通过模式设置尺寸
    private int measureWidth(int widthMeasureSpec) {
        int finalSize = 0;
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        int size = MeasureSpec.getSize(widthMeasureSpec);

        if (mode == MeasureSpec.EXACTLY) {
            finalSize = size;
        } else {
            finalSize = 250;
            if (mode == MeasureSpec.AT_MOST) {
                finalSize = Math.min(finalSize, size);
            }
        }

        return finalSize;
    }

}

讲解:

继承View类,然后实现构造方法,至少实现一个,这里我实现两个:第一个表示可以在代码中实例这个View使用,第二个表示支持xml,方便使用。

然后重写onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法,该方法传进来两个值,这两个值是从这来

android:layout_width="wrap_content"
android:layout_height="wrap_content"

代码的话就从这

LinearLayout.LayoutParams.MATCH_PARENT;
LinearLayout.LayoutParams.WRAP_CONTENT;

通过这两个值确定自定义View的大小。

查看源码,这里调用的是setMeasuredDimension()方法,所以,直接调用这个方法

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

 

setMeasuredDimension()需要两个参数,宽和高,如何来确定呢?为了方便查看,分别写了那两个方法。

 

注释是通过模式设置尺寸,模式是什么?

Android系统给我们提供了一个设计短小精悍却功能强大的类—MeasureSpec类,通过它来帮助我们测量ViewMeasureSpec类是一个32位的int值,,其中高2位为测量的模式,低30位为测量的大小。

测量的模式分3种,源码说明:

/**
 * A MeasureSpec encapsulates the layout requirements passed from parent to child.
 * Each MeasureSpec represents a requirement for either the width or the height.
 * A MeasureSpec is comprised of a size and a mode. There are three possible
 * modes:
 * <dl>
 * <dt>UNSPECIFIED</dt>没有约束,任何大小
 * <dd>
 * The parent has not imposed any constraint on the child. It can be whatever size
 * it wants.
 * </dd>
 * <dt>EXACTLY</dt>默认模式,设置具体大小或者最大match_parent
 * <dd>
 * The parent has determined an exact size for the child. The child is going to be
 * given those bounds regardless of how big it wants to be.
 * </dd>
 *
 * <dt>AT_MOST</dt> 自由大小,只要不超过父控件的大小wrap_content
 * <dd>
 * The child can be as large as it wants up to the specified size.
 * </dd>
 * </dl>

所以,我们需要获取模式,然后根据不同的模式设置宽和高的范围:

先获取到模式和最大尺寸,这里获取到的size为设备最大尺寸

 int mode = MeasureSpec.getMode(widthMeasureSpec);
 int size = MeasureSpec.getSize(widthMeasureSpec);

通过模式判断

if (mode == MeasureSpec.EXACTLY) {

如果是match_parent或具体值,那么直接赋值
            finalSize = size;
        } else {

否则,给默认值
            finalSize = 250;
如果是wrap_content,就给最小值         

   if (mode == MeasureSpec.AT_MOST) {
                finalSize = Math.min(finalSize, size);
            }
        }


分别运行效果如下:

<com.moliying.myplaystore.ui.MyView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="#0f0"
    />


非常简单,只有背景色,这是默认显示的大小,如果在自定义View中改变默认值大小,这里也会改变。

<com.moliying.myplaystore.ui.MyView
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:background="#0f0"
    />


这是具体值,精确模式。

<com.moliying.myplaystore.ui.MyView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#0f0"
    />


这是最大模式,全屏显示

<com.moliying.myplaystore.ui.MyView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#0f0"
    />


这样显示就是宽度最大,高度默认值。

以上是个人对View测量的理解,如有错误,望指出,欢迎大家交流。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值