直接继承View需要复写onMeasure方法

自定义view一般有以下几种方式

1、直接继承View类,需要重写onMeasure()和onDraw();

2、继承某个View的子类A,由于A已经复写了View的onMeasure()和onDraw(),所以一般只需要复写onDraw();

3、直接继承ViewGroup,需要复写onMeasure()和onLayout();

4、继承某个ViewGroup的子类,比如LinearLayout;

本文主要是讲解下第一种场景,即直接继承View复写onMeasure()和onDraw()。

你有没有想过直接继承View,复写onDraw()方法就好了, 为什么还要复写onMeasure()呢?

在学习“Android 开发艺术探索”中找到了答案。不过在源码中也是有说明的:

* Measure the view and its content to determine the measured width and the
* measured height. This method is invoked by {@link #measure(int, int)} and
* should be overridden by subclasses to provide accurate and efficient
* measurement of their contents.
* The base class implementation of measure defaults to the background size,
* unless a larger size is allowed by the MeasureSpec. Subclasses should
* override {@link #onMeasure(int, int)} to provide better measurements of
* their content.

上面翻译过来大概意思就是:

1、View的onMeasure()方法应该被它的子类复写,并提供对控件的精确快速测量。

2、基类(即View)的测量默认实现是背景大小,除非MeasureSpec允许更大的尺寸大小。子类应该复写onMeasure()方法来提供测试控件内容更好的测量。

我们来看View的onMeasure()方法

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

1、参数xxxMeasureSpec是父控件施加给当前View的水平或垂直空间上的要求。对于普通View来说,xxxMeasureSpec是由父控件的xxxMeasureSpec和当前View的LayoutParam参数共同决定的。那么针对不同的父容器和View本身不同的LayoutParams,View就有多种MeasureSpec。

 2、最外层的setMeasuredDimension()就是保存测量后的View的宽高。宽高最后会被保存到View的mMeasuredWidth、mMeasuredHeight,并之后在layout布局时候被使用到。

3、因此只需要看getDefaultSize()方法。

public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
}

        当前View的specMode为MeasureSpec.UNSEPCIFIED时, 则宽、高为getSuggestedMinimumXxx()的值;

        当前View的specMode为MeasureSpec.AT_MOST或MeasureSpec.EXACTLY时,则当前View的宽、高为specSize。这就会导致如果在当前View的布局中使用的是wrap_content,那么它的specMode是AT_MOST模式,在这种模式下,它的宽、高等于specSize,并且这种情况下View的SpecSize是parentSize,而parentSize是父容器中目前可以使用的大小,也就是父容器当前剩余的空间大小,很显然,View的宽、高就等于父容器当前剩下的空间大小,这种效果和在布局中使用match_parent完全一致。

        所以到目前为止也就知道了为什么直接继承View要复写onMeasure()方法了:

需要在onMeasure()方法中设置当使用wrap_content时View本身的大小,否则View的默认实现是wrap_content和match_parent效果一样。

解决方案的伪代码如下:

只需要给View指定一个默认的内部宽、高(mWidth和mHeight),并在wrap_content时设置此宽高即可。可以参考TextView和ImageView的源码的处理。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值