自定义View的测量与绘制

前言

在Android开发过程中,我们可能会经常去自定义View,在自定义View之前,我们必须了解清楚系统是如何绘制出这些View的。

一、测量

首先,我们的系统会先测量这个View的大小,以便知道该画多大的一个View。这个过程在onMeasure()方法中进行。
首先我们需要了解这样一个强大的类——MeasureSpec,它来帮助我们测量View。
MeasureSpec是一个32位的int值,高2位为测量的模式,低30位为测量的大小。

1.1三种测量模式

  • EXACTLY
    精确值模式,为我们控件的layout_width或者layout_height属性指定具体的数值,或者指定位match_parent时,用此模式。

  • AT_MOST
    最大值模式,为我们控件的layout_width或者layout_height属性指定为warp_content时,控件大小随着子控件或者内容的变化而变化,只要不超过父控件允许的最大尺寸即可。

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

默认情况下,omMeasure()只支持EXACTLY,如果自定义View时不重写onMeasure()的话,就只能使用EXACTLY,如要支持warp_content的话,则必须重写onMeasure()来指定其大小。

1.2具体使用

在onMeasure()方法中,系统最终会调用setMeasuredDimension(int measuredWidth, int measuredHeight)来设置具体的宽高值,所以我们在重写onMeasure()方法时,把测量后的宽高传入setMeasuredDimension()中就可以了。

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = getDefaultWidthSize(widthMeasureSpec);
        int height = getDefaultHeightSize(heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

我们调用getDefaultWidthSize()、getDefaultHeightSize()对宽高重新定义

private int getDefaultWidthSize(int measureSpec) {
        int result = 0;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        //准确的值 parent dx
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else {
            //最少50+padding
            result = 50 + getPaddingLeft() + getPaddingRight();
            //取指定的大小和specSize(默认为父布局这么大)的最小一个作为测量值
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

现在,我们的测量工作就做好了。

二、绘制

对于绘制工作,我们需要重写onDraw()方法,在Canvas上绘制所需要的图像。对于不同的View,绘制的难易程度也不一样,我们这里就以一个圆来介绍。

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        final int paddingLeft = getPaddingLeft();
        final int paddingRight = getPaddingRight();
        final int paddingTop = getPaddingTop();
        final int paddingBottom = getPaddingBottom();
        int width = getWidth() - paddingLeft - paddingRight;
        int height = getHeight() - paddingTop - paddingBottom;
        int radius = Math.min(width, height)/2;

        backgroundPaint.setColor(getBackgroundViewColor());
        canvas.drawCircle(paddingLeft + width / 2, paddingTop + height / 2,radius, backgroundPaint);
    }

三、布局

我们都知道,ViewGroup会去管理View,当我们的ViewGroup大小为wrap_content时,ViewGroup就需要对子View进行遍历,以便获得子View的大小来设置自己的大小。

对子View的测量就是我们前面讲到的测量方法。测量完毕后,要将子View放到合适的位置,这个过程就是子View的Layout过程,这时候就要遍历调用子View的Layout方法,并知道其具体显示的位置。

对于自定义的ViewGroup,我们需要重写其onLayout()方法来控制子View显示位置的逻辑。具体的操作我们在这里并不细讲,同学们可以参考其他的博客。

通讯录首字显示View

这是我写的一个自定义View,使用于通讯录等地方,能显示列表的首字或者前3字,可以设置背景、文字颜色等。
首字显示效果
多字显示效果

Github地址:https://github.com/Charon1997/TextCircleImageView

更多精彩内容尽在Charon的小屋

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值