探究自定义View的测量

想要自定义View,有三个方法至关重要。分别是onMeasure,onLayout,onDraw。分别表示View画多大,View画在哪里以及怎么去画View。

这篇博客记载了我学习自定义View的一些内容,由于本人对这一领域基本属于菜鸟级别,如果有理解上的错误,谢谢各位帮我指出。

首先看看View的onMeasure()源码
这里写图片描述
在onMeasure中,把宽高的测量规格保存了下来。那么方法里的参数,是从哪里传递下来的。
定位到measure()方法,onMeasure在View类中被measure()调用。而measure方法是final类型的,表示父类是不允许子类去重写的。
如果我们不复写onMeasure方法,系统会有自己的一套测量规则。它一方面不希望我们去破坏测量的架构(不允许重写measure),另一方面也提供了一次给我自己测量的机会,所以暴露出onMeasure方法。

那么onMeasure中的widthMeasureSpec和heightMeasureSpec究竟代表着什么?
在Activity中,通过代码的方式,定义一个LinearLayout,把它作为一个自定义View的父布局。

public class DemoActivity extends BaseActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(initView());
    }

    private View initView() {
        LinearLayout linear=new LinearLayout(this);
        ManualView view=new ManualView(this);
        LinearLayout.LayoutParams params=new LinearLayout.LayoutParams
        (ViewGroup.LayoutParams.MATCH_PARENT,  ViewGroup.LayoutParams.MATCH_PARENT);
        linear.addView(view,params);
        return linear;
    }
}

复写ManualView的onMeasure,并打印widthMeasureSpec和heightMeasureSpec的值。

04-04 12:16:02.405 3444-3444/asule.demo I/System.out: widthMeasureSpec:1073742592
04-04 12:16:02.413 3444-3444/asule.demo I/System.out: heightMeasureSpec:1073742958

而这两个值不知所云,它们其实是合成值,传递该值进去通过MeasureSpec.getMode和getSize可以分别获取宽高的模式和大小。
getMode方法里,是把这个合成值与十六进制数3左移30位后进行&运算。十六进制3左移30位与某个数进行&运算,只有最高位是可能有数的。也就是说只能有三种结果00,01,11。
它们分别对应着三种模式,UNSPECIFIFE,EXCATLY,AT_MOST。
其中第一种已经废弃,EXCATLY表示精确的模式,AT_MOST表示最大的模式。
打印出子View的LayoutParams指定为Match_Parent或某个具体的值时,模式和大小的值:

04-04 12:16:02.421 3444-3444/asule.demo I/System.out: 宽的模式:1
04-04 12:16:02.421 3444-3444/asule.demo I/System.out: 高的模式:1
04-04 12:16:02.425 3444-3444/asule.demo I/System.out: 宽的大小:768
04-04 12:16:02.429 3444-3444/asule.demo I/System.out: 高的大小:1134

发现它们的模式都是精确的模式,且宽高是屏幕的宽高。
现在把子View的LayoutParams指定为Wrap_Content,打印的结果是:

04-04 12:26:21.553 10089-10089/asule.demo I/System.out: 宽的模式:2
04-04 12:26:21.553 10089-10089/asule.demo I/System.out: 高的模式:2
04-04 12:26:21.553 10089-10089/asule.demo I/System.out: 宽的大小:768
04-04 12:26:21.553 10089-10089/asule.demo I/System.out: 高的大小:1134

模式是最大的模式,宽高是屏幕的宽高。

如果传递过来的模式是精确的,那么直接使用传过来的大小。
如果传递过来的模式是最大的,先测量当前子View的size,如果当前子View的size小于传过来的size,那么直接使用测量的size,如果测量的size大于传过来的size,期望使用父类传过类的size。

附上一张图:

探究自定义View的测量_第1张图片

父View的模式为EXACTLY,
子View的宽高是某个具体的dp值,那么它的模式是EXACTLY精确的,大小是它指定的大小。
而如果是Wrap_Content,它的模式是最大的AT_MOST,大小size是父View的大小。
而如果是Match_Parent,模式是EXACTLY,大小是父view的大小。
这似乎很好理解,父View的模式是精确的,表示它的宽高均是Match_Parent,子View如果是某个具体的值,它的宽高就是具体的值,模式是精确的。
如果是Wrap_Content,模式是最大的,宽高为父类的size。

你可能感兴趣的:(博客)