欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

一个简单的自定义View

程序员文章站 2022-06-08 16:12:22
...

一般来说,对于自定义View,我们有一套自己的开发策略,大致会经过如下几个步骤:

1. 整理自定义View需要定义的属性,这个主要是考虑到View的复用,将它一些可以更改设定的属性提取出来,这些属性主要指非layout_开头的属性,将这些属性以View的类型定义在attrs.xml文件中,如下代码所示:

    <declare-styleable name="CustomView">
        <attr name="foreColor" format="color" />
        <attr name="backColor" format="color" />
        <attr name="strokeWidth" format="integer|dimension" />
        <attr name="circleSpeen" format="integer" />
        <attr name="circleRadius" format="integer|dimension" />
    </declare-styleable>

这个过程不是必须的,因为我们可以在自定义View的类中增加相关属性成员变量,然后提供设置这些属性的方法,运行时动态设定,也是OK的,如下代码所示:

    // 定义属性成员变量
    private int mForeColor;
    private int mBackColor;


    // 提供对外设置属性的方法
    public void setBackColor(int backColor) {
        this.mBackColor = backColor;
    }

    public void setForeColor(int foreColor) {
        this.mForeColor = foreColor;
    }

    ...

    // 外界根据自定义View对象来设置其属性
    mFirstCircle.setBackColor(Color.MAGENTA);
    mFirstCircle.setForeColor(Color.GREEN);

2. 如果有在attrs.xml中设置属性,那么在自定义View的构造函数中,需要将这些自定义的属性读进来,方便后续画图的时候用上,读取过程如下所示:

    public CustomView(Context context) {
        super(context, null);
    }
    public CustomView(Context context, @Nullable AttributeSet attrs) {
        // 注意此处不能调super方法,必须用我们自己写的构造方法,不然就白写了
        this(context, attrs, 0);
    }

    public CustomView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray typedAttrbites = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
        mForeColor = typedAttrbites.getColor(R.styleable.CustomView_foreColor, Color.GRAY);
        mBackColor = typedAttrbites.getColor(R.styleable.CustomView_backColor, Color.BLUE);
        mCircleSpeed = typedAttrbites.getInt(R.styleable.CustomView_circleSpeen, 20);
        mCircleRadius = typedAttrbites.getInt(R.styleable.CustomView_circleRadius, 30);
        mCircleStrokeWidth = typedAttrbites.getInt(R.styleable.CustomView_strokeWidth, 30);
        mPaint = new Paint();
        mCircleProcess = 0;
        mKeepGoing = true;
        new Thread(mGoingRunnable).start();
    }

3. 重写onMeasure方法,来确定自定义View的大小,大家都知道,尺寸计算模式有三种:

MeasureSpec.EXACTLY:顾名思义,精确尺寸的意思,适用于给自定义View在layout文件中设置精确的宽高、或者使用match_parent的情况。

MeasureSpec.AT_MOST:自定义View有多大就设置多大的尺寸,适用于宽高设置为wrap_content的情况。

MeasureSpec.UNSPECIFIED:SDK中的解释:The parent has not imposed any constraint on the child. It can be whatever size it wants. 我们写布局时很少出现宽高完全未知的情况,没用过,不考虑。

有了这个基本的概念后,我们对于确定自定义View的大小就比较方便啦,代码如下所示:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int desireWidth;
        // 如果是精确尺寸,那设置多大就是多大
        if (widthMode == MeasureSpec.EXACTLY) {
            desireWidth = widthSize;
        } else {
            // 如果是wrap_content尺寸,那么根据自定义View的情况来计算高度
            // 比如此处自定义View是一个圆环,那么它的宽度就是:半径*2+圆环宽度
            desireWidth = mCircleRadius * 2 + mCircleStrokeWidth;
        }

        int desireHeight;
        if (heightMode == MeasureSpec.EXACTLY) {
            desireHeight = heightSize;
        } else {
            desireHeight = mCircleRadius * 2 + mCircleStrokeWidth;
        }

        setMeasuredDimension(desireWidth, desireHeight);
    }

4. 重写onDraw,尺寸确定后,就可以根据需求画图了,onDraw方法提供一个参数Canvas,即画布的意思,它提供很多画各种不同形状的API,比如画圆drawCircle、画矩形drawRect等等,加上画笔Paint(一般定义为成员变量,在构造方法中初始化),就可以想怎么画就怎么画了,如下代码画的是一个待进度的圆环。

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int circleX = getWidth() / 2;
        int circleY = getHeight() / 2;
        mPaint.setColor(mBackColor);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(mCircleStrokeWidth);
        mPaint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(circleX, circleY, mCircleRadius, mPaint);

        mPaint.setColor(mForeColor);
        RectF rectF = new RectF(circleX - mCircleRadius, circleY - mCircleRadius,
                circleX + mCircleRadius, circleY + mCircleRadius);
        canvas.drawArc(rectF, -90, mCircleProcess, false, mPaint);
    }

至此,简单的自定义View绘制基本就完成了。

过程不复杂,主要是开发之前要想好需要定义哪些属性,属性确定后,想好宽高怎么计算,然后设定好绘制过程,查好相关的API就可以了,一步一步来,先确定好思路再下手开发~