一个简单的自定义View

一般来说,对于自定义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就可以了,一步一步来,先确定好思路再下手开发~

猜你喜欢

转载自blog.csdn.net/ZX_XI/article/details/81358895