Android自定义View之仪表盘
欢迎访问我的博客
又是新系列(坑) 感觉都很零碎,能积累一些是一些了
背景
随着项目开发 越来越多的需求被摆在面前 其中不免涉及到定制的功能
其中仪表盘也是一个很常用的功能
效果图
设计过程
外侧渐变圆环
外侧刻度盘及文字显示
指针显示
内部圆环及文字展示
代码实现
自定义组件显示优化
设置自定义组件的时候要优化组件的高度
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int heitht = width / 2 / 4 * 5;
initIndex(width / 2);
//优化组件高度
setMeasuredDimension(width, heitht);
}
onDraw()过程
protected void onDraw(Canvas canvas) {
//禁用硬件加速
setLayerType(LAYER_TYPE_SOFTWARE, null);
//外侧颜色指示圆环
initRing(canvas);
//刻度文字
initScale(canvas);
//指针
initPointer(canvas);
//提示内容
initText(canvas);
}
主要还是这个四个绘制的过程
外侧颜色指示圆环
- 首先绘制的前一部分的红黄渐变圆环
这个圆环并不是一个180度的圆环 而是一个两百度的圆环 下侧再实现水平的效果 - 绘制后一部分的绿色渐变圆环
- 修正底部的效果 修改成水平的效果
- 绘制内部半圆 遮盖住渐变的半圆
private void initRing(Canvas canvas) {
paint.setAntiAlias(true);
paint.setStrokeWidth(2);
canvas.save();
//canvas中心移动到中间
canvas.translate(canvas.getWidth()/2, r);
//前100红黄渐变圆环
paint.setStyle(Paint.Style.FILL);
//设置渐变的颜色范围
int[] colors = {Color.parseColor("#F95A37"), Color.parseColor("#f9cf45")};
//设置的渐变起止位置
float[] positions = {0.5f - 10f/180f * 0.5f, 0.5f + 0.5f * 5f / 6f};
//设置渐变的蒙版
SweepGradient sweepGradient = new SweepGradient(0, 0, colors, positions);
paint.setShader(sweepGradient);
rect = new RectF( -length, -length, length, length);
//绘制圆环
canvas.drawArc(rect, 170, 10f + 180f / 6f * 5f, true, paint);
//100之后绿色渐变圆环
paint.setStyle(Paint.Style.FILL);
canvas.rotate(10,0f,0f);
int[] colors2 = {Color.parseColor("#79D062"), Color.parseColor("#3FBF55")};
float[] positions2 = {0.5f + 0.5f * ( 144f / 180f), 1.0f};
sweepGradient = new SweepGradient(0, 0, colors2, positions2);
paint.setShader(sweepGradient);
rect = new RectF( -length, -length, length, length);
canvas.drawArc(rect, 180f + 180f * (140f / 180f), 180f / 6 + 10, true, paint);
canvas.restore();
canvas.save();
canvas.translate(canvas.getWidth()/2, r);
//绘制描边效果的画笔
strokePain = new Paint(paint);
strokePain.setColor(0x3f979797);
strokePain.setStrokeWidth(10);
strokePain.setShader(null);
strokePain.setStyle(Paint.Style.STROKE);
canvas.drawArc(rect, 170, 200, true, strokePain);
canvas.restore();
canvas.save();
canvas.translate(canvas.getWidth()/2, r);
//底边水平
paint.setShader(null);
paint.setColor(backGroundColor);
paint.setStyle(Paint.Style.FILL);
canvas.drawRect(-length , (float) (Math.sin(Math.toRadians(10) ) * length /3f * 2f), length , (float) (Math.sin(Math.toRadians(10)) * length + 100) , paint);
canvas.drawRect(-length , (float) (Math.sin(Math.toRadians(10) ) * length /3f * 2f), length , (float) (Math.sin(Math.toRadians(10) ) * length /3f * 2f) , strokePain);
//内部背景色填充
paint.setColor(backGroundColor);
paint.setShader(null);
rect = new RectF( - (length - length / 3f - 2), -(length / 3f * 2f - 2), length - length / 3f -2 , length / 3f * 2f - 2);
canvas.drawArc(rect, 170, 200, true, strokePain);
canvas.drawArc(rect, 0, 360, true, paint);
}
外侧刻度盘及文字显示
旋转画布绘制对应角度的显示及刻度
private void initScale(Canvas canvas) {
canvas.restore();
canvas.save();
canvas.translate(canvas.getWidth()/2, r);
paint.setColor(Color.parseColor("#999999"));
tmpPaint = new Paint(paint); //刻度画笔对象
tmpPaint.setStrokeWidth(1);
tmpPaint.setTextSize(35);
tmpPaint.setTextAlign(Paint.Align.CENTER);
canvas.rotate(-90,0f,0f);
float y = length;
y = - y;
int count = 12; //总刻度数
paint.setColor(backGroundColor);
float tempRou = 180 / 12f;
//每次旋转的角度
paint.setColor(Color.WHITE);
paint.setStrokeWidth(5);
//绘制刻度和百分比
for (int i = 0 ; i <= count ; i++){
if (i % 2 == 0 ) {
canvas.drawText(String.valueOf((i) * 10), 0, y - 20f, tmpPaint);
}
canvas.drawLine(0f, y , 0, y + length / 15, paint);
canvas.rotate(tempRou,0f,0f);
}
}
指针显示
指针显示的比较简单也是唯二需要变化的之一
指针的绘制比较简单 根据传入的角度(百分比)旋转对应的角度 填充绘制一个三角形
private void initPointer(Canvas canvas) {
paint.setColor(Color.BLACK);
canvas.restore();
canvas.save();
canvas.translate(canvas.getWidth()/2, r);
float change;
if (perPoint < 1 ){
change = perPoint * 180;
}else {
change = 180;
}
//根据参数得到旋转角度
canvas.rotate(-90 + change,0f,0f);
//绘制三角形形成指针
Path path = new Path();
path.moveTo(0 , pointLength);
path.lineTo(-10 , 0);
path.lineTo(10,0);
path.lineTo(0 , pointLength);
path.close();
canvas.drawPath(path, paint);
}
内部圆环及文字展示
先绘制一个带阴影的圆环 再居中绘制提示的文本信息
private void initText(Canvas canvas) {
//抗锯齿
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
canvas.restore();
canvas.save();
canvas.translate(canvas.getWidth()/2, r);
float rIndex = length ;
//设置文字展示的圆环
paint.setColor(Color.parseColor("#eeeeee"));
paint.setShader(null);
paint.setShadowLayer(5, 0, 0, 0x54000000);
rect = new RectF( - (rIndex/ 3 ), - (rIndex / 3), rIndex / 3, rIndex / 3);
canvas.drawArc(rect, 0, 360, true, paint);
paint.clearShadowLayer();
canvas.restore();
canvas.save();
canvas.translate(canvas.getWidth()/2f , r);
textPaint.setStrokeWidth(1);
textPaint.setAntiAlias(true);
textPaint.setTextSize(60);
textPaint.setColor(Color.parseColor("#fc6555"));
textPaint.setTextAlign(Paint.Align.RIGHT);
//判断指数变化及颜色设定
int _per = (int) (per * 120);
if (_per < 60){
textPaint.setColor(Color.parseColor("#ff6450"));
}else if (_per < 100) {
textPaint.setColor(Color.parseColor("#f5a623"));
}else {
textPaint.setColor(Color.parseColor("#79d062"));
}
float swidth = textPaint.measureText(String.valueOf(_per));
//计算偏移量 是的数字和百分号整体居中显示
swidth = (swidth - (swidth + 22) / 2);
canvas.translate( swidth , 0);
canvas.drawText("" + _per, 0, 0, textPaint);
textPaint.setTextSize(30);
textPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("%" , 0, 0, textPaint);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setColor(Color.parseColor("#999999"));
canvas.restore();
canvas.save();
canvas.translate(canvas.getWidth()/2 , r + length / 3 /2 );
canvas.drawText("完成率" , 0, 0, textPaint);
}
更新动画
使用ValueAnimator实现指针的转动动画效果
public void cgangePer(float per ){
this.perOld = this.per;
this.per = per;
ValueAnimator va = ValueAnimator.ofFloat(perOld,per);
va.setDuration(1000);
va.setInterpolator(new OvershootInterpolator());
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
perPoint = (float) animation.getAnimatedValue();
invalidate();
}
});
va.start();
}
这个仪表盘的实现就完成了 具体的代码可以查看我的github