Android中自定义View中的Paint,Canvas,RectF,Path

每一个View的绘制过程都必须经历三个最主要的过程,也就是onMeasure()、onLayout() 和 onDraw()

一般在构造方法中做这样的事情:


	public RoundView(Context context) {
		this(context, null);
	}

	public RoundView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public RoundView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		// 获取我们自定义的样式属性
		TypedArray array = context.getTheme().obtainStyledAttributes(attrs,R.styleable.RoundView, defStyleAttr, 0);
		int n = array.getIndexCount();
		for (int i = 0; i < n; i++) {
			int attr = array.getIndex(i);
			switch (attr) {
			case R.styleable.RoundView_titleColor:
				// 默认颜色设置为黑色
				textColor = array.getColor(attr, Color.BLUE);
				break;
			case R.styleable.RoundView_lineColor:
				lineColor = array.getColor(attr, Color.BLUE);
				break;
			}
		}
		array.recycle();
		init();//在此方法内初始化自定义View中的各个成员变量;  
	}



接下来   onMeasure()、中做这样的事情:

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		int widthSize = MeasureSpec.getSize(widthMeasureSpec);
		int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		int heightSize = MeasureSpec.getSize(heightMeasureSpec);
		int width;
		int height;
		// 如果布局里面设置的是固定值,这里取布局里面的固定值;如果设置的是match_parent,则取父布局的大小
		if (widthMode == MeasureSpec.EXACTLY) {
			width = widthSize;
		} else {
		<span style="white-space:pre">	</span>// 如果布局里面没有设置固定值,这里取布局的宽度的1/2
			width = widthSize * 1 / 2;
		}
		if (heightMode == MeasureSpec.EXACTLY) {
			height = heightSize;
		} else {
			// 如果布局里面没有设置固定值,这里取布局的高度的3/4
			height = heightSize * 3 / 4;
		}
		widthBg = width;
		heightBg = height;
		setMeasuredDimension(width, height);   //设置该自定义View的宽高
		startAnim();  //启动动画...
	}



onLayout() 方法是ViewGroup中子View的布局方法,用于放置子View的位置。放置子View很简单,只需在重写onLayout方法,然后获取子View的实例,调用子View的layout方法实现布局。 在这里没写。


接下来是onDraw()方法 用来绘制图形;

	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		// 绘制最底层的背景
		radiusBg = widthBg / 20;  // 背景的坐标
		pathBg.moveTo(0, heightBg);  设置绘制的开始点(x,y)。
		pathBg.lineTo(0, radiusBg);  //添加一条从一点到指定点(x,y)的线,如果没有移至,则自动设置为(0,0)。
		pathBg.quadTo(0, 0, radiusBg, 0);// 用于绘制圆滑曲线 mPath.quadTo(x1, y1, x2, y2) (x1,y1) 为控制点,(x2,y2)为结束点
		pathBg.lineTo(widthBg - radiusBg, 0);
		pathBg.quadTo(widthBg, 0, widthBg, radiusBg);
		pathBg.lineTo(widthBg, heightBg);
		pathBg.lineTo(0, heightBg);
		backgroundPaint.setColor(Color.WHITE);
		canvas.drawPath(pathBg, backgroundPaint);
	}



cubicTo 同样是用来实现贝塞尔曲线的。

mPath.cubicTo(x1, y1, x2, y2, x3, y3) (x1,y1) 为控制点,(x2,y2)为控制点,(x3,y3) 为结束点。




arcTo 用于绘制弧线(实际是截取圆或椭圆的一部分)。

mPath.arcTo(ovalRectF, startAngle, sweepAngle) , ovalRectF为椭圆的矩形,startAngle 为开始角度,sweepAngle 为结束角度。

mRectF = new RectF(10, 10, 600, 600);
mPath.arcTo(mRectF, 0, 90);
canvas.drawPath(mPath, mPaint);

由于new RectF(10, 10, 600, 600)为正方形,又截取 0 ~ 90 度 ,则所得曲线为四分之一圆的弧线。

效果如如下




另外贴上RoundView的效果图,以及完整代码:


package com.panda.app.customer;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.MeasureSpec;

import com.example.mylibary.R;

@SuppressLint("NewApi")
public class RoundView extends View {

    private int mySize, rank, averageSize;
    private String myaverageTxt;


    //字体颜色,大小,竖线的颜色
    private int textColor, lineColor;
    //背景的画笔
    private Paint backgroundPaint;
    //背景的坐标
    private int radiusBg, widthBg, heightBg;
    private Path pathBg, linePath;
    //圆弧的画笔
    private Paint arcPaint;
    private RectF arcRect;
    //数字的画笔
    private Paint textPaint;
    private PathEffect effects;

    //虚线的画笔
    private Paint linePaint;


    //圆角竖条的距离,高度,平均高度
    private float rectSize, rectAgHeight;
    //圆角竖条的画笔
    private Paint rectPaint;
    private Path rectPath;

    //底部波纹
    private Paint weavPaint;
    private Path weavPath;

    //动画实现
    //动画效果的添加
    private AnimatorSet animSet;
    private int walkNum, rankNum;
    private float arcNum;


    public void setMySize(int mySize) {
        this.mySize = mySize;
    }

    public void setRank(int rank) {
        this.rank = rank;
    }

    public void setAverageSize(int averageSize) {
        this.averageSize = averageSize;
    }

    public RoundView(Context context) {
        this(context, null);
    }

    public RoundView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RoundView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //获取我们自定义的样式属性
        TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RoundView, defStyleAttr, 0);
        int n = array.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = array.getIndex(i);
            switch (attr) {
                case R.styleable.RoundView_titleColor:
                    // 默认颜色设置为黑色
                    textColor = array.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.RoundView_lineColor:
                    lineColor = array.getColor(attr, Color.BLACK);
                    break;
            }

        }
        array.recycle();
        init();
    }

    //初始化操作
    private void init() {
        pathBg = new Path();
        backgroundPaint = new Paint();
        backgroundPaint.setAntiAlias(true);
        arcPaint = new Paint();
        arcPaint.setAntiAlias(true);
        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        linePaint = new Paint();
        linePaint.setAntiAlias(true);
        linePath = new Path();
        effects = new DashPathEffect(new float[]{5,5}, 1);
        rectPaint = new Paint();
        rectPaint.setAntiAlias(true);
        rectPath = new Path();
        weavPaint = new Paint();
        weavPaint.setAntiAlias(true);
        weavPath = new Path();
        animSet = new AnimatorSet();
    }


    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
	@SuppressLint("NewApi")
	@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制最底层的背景
        radiusBg = widthBg / 20;
        pathBg.moveTo(0, heightBg);
        pathBg.lineTo(0, radiusBg);
        pathBg.quadTo(0, 0, radiusBg, 0);
        pathBg.lineTo(widthBg - radiusBg, 0);
        pathBg.quadTo(widthBg, 0, widthBg, radiusBg);
        pathBg.lineTo(widthBg, heightBg);
        pathBg.lineTo(0, heightBg);
        backgroundPaint.setColor(Color.WHITE);
        canvas.drawPath(pathBg, backgroundPaint);

        //绘制圆弧
        arcPaint.setStrokeWidth(widthBg / 20);
        //设置空心
        arcPaint.setStyle(Paint.Style.STROKE);
        //防抖动
        arcPaint.setDither(true);
        //连接处为圆弧
        arcPaint.setStrokeJoin(Paint.Join.ROUND);
        //画笔的笔触为圆角
        arcPaint.setStrokeCap(Paint.Cap.ROUND);
        arcPaint.setColor(lineColor);
        //圆弧范围
        arcRect = new RectF(widthBg * 1 / 4, widthBg * 1 / 4, widthBg * 3 / 4, widthBg * 3 / 4);
        //绘制背景大圆弧
        canvas.drawArc(arcRect, 120, 300, false, arcPaint);
        arcPaint.setColor(textColor);
        //绘制分数小圆弧
        canvas.drawArc(arcRect, 120, arcNum, false, arcPaint);

        //绘制圆圈内的数字
        textPaint.setColor(textColor);
        textPaint.setTextSize(widthBg / 10);
        canvas.drawText(String.valueOf(walkNum), widthBg * 3 / 8, widthBg * 1 / 2 + 20, textPaint);
        //绘制名次
        textPaint.setTextSize(widthBg / 15);
        canvas.drawText(String.valueOf(rankNum), widthBg * 1 / 2 - 15, widthBg * 3 / 4 + 10, textPaint);

        //绘制其他文字
        textPaint.setColor(lineColor);
        textPaint.setTextSize(widthBg / 25);
        canvas.drawText("截止13:45已走", widthBg * 3 / 8 - 10, widthBg * 5 / 12 - 10, textPaint);
        canvas.drawText("好友平均2781步", widthBg * 3 / 8 - 10, widthBg * 2 / 3 - 20, textPaint);
        canvas.drawText("第", widthBg * 1 / 2 - 50, widthBg * 3 / 4 + 10, textPaint);
        canvas.drawText("名", widthBg * 1 / 2 + 30, widthBg * 3 / 4 + 10, textPaint);

        //绘制圆圈外的文字
        canvas.drawText("最近7天", widthBg * 1 / 15, widthBg, textPaint);
        myaverageTxt = String.valueOf(averageSize);
        canvas.drawText("平均", widthBg * 10 / 15 - 15, widthBg, textPaint);
        canvas.drawText(myaverageTxt, widthBg * 11 / 15, widthBg, textPaint);
        canvas.drawText("步/天", widthBg * 12 / 15 + 20, widthBg, textPaint);

        //绘制虚线
        linePaint.setStyle(Paint.Style.STROKE);
        linePaint.setStrokeWidth(2);
        linePaint.setColor(lineColor);
        linePath.moveTo(widthBg * 1 / 15, widthBg + 80);
        linePath.lineTo(widthBg * 14 / 15, widthBg + 80);
        linePaint.setPathEffect(effects);
        canvas.drawPath(linePath, linePaint);

        rectSize = widthBg / 12;
        rectAgHeight = widthBg / 10;
        //绘制虚线上的圆角竖线
        for (int i = 0; i < 4; i++) {
            rectPaint.setStrokeWidth(widthBg / 25);
            rectPaint.setStyle(Paint.Style.STROKE);
            rectPaint.setStrokeJoin(Paint.Join.ROUND);
            rectPaint.setStrokeCap(Paint.Cap.ROUND);
            float startHeight = widthBg + 90 + rectAgHeight;
            rectPath.moveTo(rectSize, startHeight);
            double percentage = Double.valueOf(4) / Double.valueOf(averageSize);
            double height = percentage * rectAgHeight;
            rectPath.lineTo(rectSize, (float) (startHeight - height));
            rectPaint.setColor(textColor);
            canvas.drawPath(rectPath, rectPaint);
            //绘制下方的文字
            textPaint.setColor(lineColor);
            canvas.drawText("0" + (i + 1) + "日", rectSize - 25, startHeight + 50, textPaint);
            rectSize += widthBg / 7;
        }
        //绘制底部波纹
        weavPaint.setColor(textColor);
        weavPath.moveTo(0, heightBg);
        weavPath.lineTo(0, heightBg * 10 / 12);
        weavPath.cubicTo(widthBg * 1 / 10, heightBg * 10 / 12, widthBg * 3 / 10, heightBg * 11 / 12, widthBg, heightBg * 10 / 12);
        weavPath.lineTo(widthBg, heightBg);
        weavPath.lineTo(0, heightBg);
        canvas.drawPath(weavPath, weavPaint);

        //绘制底部文字
        weavPaint.setColor(Color.WHITE);
        weavPaint.setTextSize(widthBg / 20);
        canvas.drawText("成绩不错,继续努力哟!", widthBg * 1 / 10 - 20, heightBg * 11 / 12 + 50, weavPaint);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height;
        //如果布局里面设置的是固定值,这里取布局里面的固定值;如果设置的是match_parent,则取父布局的大小
        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {

            //如果布局里面没有设置固定值,这里取布局的宽度的1/2
            width = widthSize * 1 / 2;
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            //如果布局里面没有设置固定值,这里取布局的高度的3/4
            height = heightSize * 3 / 4;
        }
        widthBg = width;
        heightBg = height;
        setMeasuredDimension(width, height);
        startAnim();

    }

    @SuppressLint("NewApi")
	private void startAnim() {
        //步数动画的实现
        ValueAnimator walkAnimator = ValueAnimator.ofInt(0, mySize);
        walkAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @SuppressLint("NewApi")
			@TargetApi(Build.VERSION_CODES.HONEYCOMB)
			@Override
            public void onAnimationUpdate(ValueAnimator animation) {
                walkNum = (int) animation.getAnimatedValue();
                postInvalidate();
            }
        });
        //排名动画的实现
        ValueAnimator rankAnimator = ValueAnimator.ofInt(0, rank);
        rankAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                rankNum = (int) animation.getAnimatedValue();
                postInvalidate();
            }
        });

        double size = mySize;
        double avgSize = averageSize;
        if (size > avgSize) {
            size = avgSize;
        }
        //圆弧动画的实现
        ValueAnimator arcAnimator = ValueAnimator.ofFloat(0, (float) (size / avgSize * 300));
        arcAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                arcNum = (float) animation.getAnimatedValue();
                postInvalidate();
            }
        });
        animSet.setDuration(3000);
        animSet.playTogether(walkAnimator, rankAnimator, arcAnimator);
        animSet.start();
    }

    public void reSet(int mysize, int myrank, int myaverageSize) {
        walkNum = 0;
        arcNum = 0;
        rankNum = 0;
        mySize = mysize;
        rank = myrank;
        averageSize = myaverageSize;
        startAnim();
    }
}





你可能感兴趣的:(Android中自定义View中的Paint,Canvas,RectF,Path)