需求要写一个环形百分比,是这样的:
第一张是UI图,第二张做出来后,在layout上渲染出来的图。
首先,需要知道android的坐标系是怎么样的,左上为(0,0)往右x轴增大,往下y轴增大
然后就直接上代码啦,变量不多,都有注释,也没有弄AttributeSet,想弄layout设置参数的,可以自己加一下,style文件写好配置,layout文件里设置,然后自定义控件里获取。
public class CirclePercentView extends View {
// 圆画笔
private Paint circlePaint;
// 圆环画笔
private Paint ringPaint;
// 百分数画笔
private Paint textPaint;
//动态设置的底环颜色
private String circleColor;
//动态设置圆环颜色
private String ringColor;
private float percent = 35.2f;//设一个初始的百分比,超过0的话,能在Android studio layout文件下直接看到效果
//控件宽度
private int mWidth;
//画圆环需要的RectF,因为只需要生成一次,假如刷新界面的话,他就会一直生成,虽然不影响,但是把它拿出来全局
private RectF rectF;
//文字大小
private float textSize;
//圆环宽度
private int stroke;
public CirclePercentView(Context context) {
super(context);
init();
}
public CirclePercentView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public CirclePercentView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public CirclePercentView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
circlePaint = new Paint();
circlePaint.setAntiAlias(true);//设置抗锯齿
circlePaint.setStyle(Paint.Style.STROKE);//设置绘画风格为边框
//这个是个dp转px的方法,不严格的话可以随便写个10,12,15随意
stroke = AppUtils.dip2px(getContext(), 10);
circlePaint.setStrokeWidth(stroke);//设置边框宽度
circlePaint.setColor(getResources().getColor(R.color.blue_ff));//设置颜色,可以替换成Color.parseColor("#xxxxxx")
ringPaint = new Paint();
ringPaint.setAntiAlias(true);
ringPaint.setStyle(Paint.Style.STROKE);
ringPaint.setStrokeWidth(stroke);
ringPaint.setColor(getResources().getColor(R.color.blue_24));//可以替换成Color.parseColor("#xxxxxx")
textPaint = new Paint();
textPaint.setStyle(Paint.Style.FILL);//设置风格为充满
textPaint.setAntiAlias(true);
textPaint.setColor(getResources().getColor(R.color.black));
textSize = AppUtils.dip2px(getContext(), 14);
textPaint.setTextSize(textSize);
}
/**
* 设置颜色和中间的文字
* @param color 这个color是字符串,不带#号6位
* @param percent 传0-100的
*/
public void setTextColor(String color, double percent) {
circleColor = "#80" + color;//让背景环颜色为环颜色透明一些,
ringColor = "#" +color;
startAnimator((float) percent);//开启动画
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = getWidth();
int mHeight = getHeight();
if(mWidth > mHeight){
mWidth = mHeight;//获取最短的一边
}
rectF = new RectF();//rectF为画扇形所需要的矩形,扇形会在矩形内
//rectF长宽为right-left或bottom-top,因为是正方形,都一样
//因为画笔使用了stroke,他半径不为控件的一半了,不设置stroke的话长宽都是是圆半径*2,
// 长宽 = mWidth - stroke,
//因为rectF范围不是满控件了,所以位置不能是0 0不然画出来,会偏左上角,
// 为了使rectF居中,都往右下角偏移了stroke/2,所以也要少减stroke/2,
rectF.left = stroke/2;
rectF.top = stroke/2;
//知道了长宽,通过左上可以计算右下
rectF.right = mWidth - stroke/2;
rectF.bottom = mWidth - stroke/2;
//可以自己设置成rectF.left = 0; rectF.top = 0; rectF.right = mWidth - stroke; rectF.bottom = mWidth - stroke
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (circleColor != null) {
circlePaint.setColor(Color.parseColor(circleColor));//如果有颜色的话,设置颜色
}
if (ringColor != null) {
ringPaint.setColor(Color.parseColor(ringColor));//如果有颜色的话,设置颜色颜色
}
//画外层圆环 当画笔设置了 StrokeWidth 时,圆的半径=内圆的半径+StrokeWidth/2
canvas.drawCircle(mWidth/2, mWidth/2, (mWidth/2 - stroke) + stroke/2, circlePaint);
float point = percent*360/100;
if (point > 360) {
point = 360;
}
//rectF扇形范围,-90,从上开始绘画,比如改成0,就是从左开始绘画,顺时针,point绘画多少角度
// false是去掉扇形的半径绘画,ringPaint画笔
canvas.drawArc(rectF, -90, point, false, ringPaint);
String str = percent + "%";//需要自定义文字的话,就定义一个全局str,从外面传进来
float textWid = textPaint.measureText(str);
//str文字,第二个和第三个参数是为了使他居中mWidth/2 - textWid/2:从x轴的哪里开始绘画,应该很好计算
//mWidth/2 + textSize/2 之所以是+是因为文字是从左下角开始绘制的,开始以为是从上到下,写成了-,就偏上了
canvas.drawText(str, mWidth/2 - textWid/2, mWidth/2 + textSize/2, textPaint);
}
private void startAnimator(float process) {
ValueAnimator animator = ValueAnimator.ofFloat(0, process);//从百分0到process的过程
animator.setDuration(800);//持续时间
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
percent = (Float) animation.getAnimatedValue();//当这个动画过程进行过程中,获取过程中的数值
BigDecimal b = new BigDecimal((Float) animation.getAnimatedValue());
percent = b.setScale(2, ROUND_DOWN).floatValue();//保留两位小数
invalidate();//重绘,会重新走onDraw,onDraw里是根据percent绘画的,所以界面会改变
}
});
animator.start();
}
}
View是先走onMeasure ->onSizeChanged->onDraw 因此,在onSizeChange获取控件宽度。
动画的原理是,让角度慢慢增长,通过使百分比从0开始,一段时间增长至所设置的百分比,在百分比改变的时候,将界面进行重绘
当画圆和扇形,画笔设置成了stroke,它的半径就不是想当然的控件宽度除以2了,假如是像上面代码圆环充满控件,它的宽度就是控件宽度-环的宽度除以2,这个是要自己计算的,同理能得出画扇形所需要的矩形,注意让矩形位置居中。自己画了张图,额,字比较丑,将就一下