Android自定义View之仪表盘

Android自定义View之仪表盘

欢迎访问我的博客

又是新系列(坑) 感觉都很零碎,能积累一些是一些了

背景

随着项目开发 越来越多的需求被摆在面前 其中不免涉及到定制的功能
其中仪表盘也是一个很常用的功能

效果图

《Android自定义View之仪表盘》 效果图

设计过程

外侧渐变圆环

《Android自定义View之仪表盘》 外侧圆环效果

外侧刻度盘及文字显示

《Android自定义View之仪表盘》 外侧刻度盘及文字显示

指针显示

《Android自定义View之仪表盘》 指针显示

内部圆环及文字展示

《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);
  }

主要还是这个四个绘制的过程

外侧颜色指示圆环

  1. 首先绘制的前一部分的红黄渐变圆环
    这个圆环并不是一个180度的圆环 而是一个两百度的圆环 下侧再实现水平的效果
  2. 绘制后一部分的绿色渐变圆环
  3. 修正底部的效果 修改成水平的效果
  4. 绘制内部半圆 遮盖住渐变的半圆
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

    原文作者:又似在水一方
    原文地址: https://www.jianshu.com/p/b872aec85059
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞