欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android自定义View实现仿驾考宝典显示分数效果(收藏)

程序员文章站 2023-12-12 15:10:52
小编最近发现,一些炫酷的view效果,通过需要自定义view和属性动画结合在一起,才能更容易的实现。 实现的效果图如下: 所用的知识有: (1)自定义view中...

小编最近发现,一些炫酷的view效果,通过需要自定义view和属性动画结合在一起,才能更容易的实现。

实现的效果图如下:

Android自定义View实现仿驾考宝典显示分数效果(收藏)

所用的知识有:

(1)自定义view中的 path ,主要用来绘制指示块。

(2)属性动画-valueanimator,并设置属性动画的监听器。

(3)根据属性动画是否结束的标志,决定是否绘制分数对应的描述文本内容。

实现步骤:

继承自view,在构造函数中获取自定义属性和初始化操作(初始化画笔)

private void obtainattrs(context context, attributeset attrs) {
    typedarray typedarray = context.obtainstyledattributes(attrs, r.styleable.scoreview);
    linelength = typedarray.getdimension(r.styleable.scoreview_linelength, dp2px(10));
    linecolor = typedarray.getcolor(r.styleable.scoreview_linecolor, color.white);
    typedarray.recycle();
  }
  private void init() {
    arrowpaint = createpaint(color.white, 0, paint.style.fill, 0);
    arcpaint = createpaint(linecolor, dp2px(1), paint.style.stroke, 0);
    bgpaint = createpaint(linecolor, dp2px(1), paint.style.fill, 0);
    reachprogresspaint = createpaint(color.white, dp2px(1), paint.style.fill, 0);
    arcreachpaint = createpaint(color.white, dp2px(1), paint.style.stroke, 0);
    scoretextpaint = createpaint(color.white, 0, paint.style.stroke, dp2px(26));
    desctextpaint = createpaint(color.white, 0, paint.style.stroke, dp2px(16));
  }

其中初始化画笔抽取到一个函数中:

private paint createpaint(int color, int strokewidth, paint.style style, float textsize) {
    paint paint = new paint(paint.anti_alias_flag);
    paint.setcolor(color);
    paint.setstrokewidth(strokewidth);
    paint.setstyle(style);
    paint.setstrokejoin(paint.join.round);
    paint.setstrokecap(paint.cap.round);
    paint.settextsize(textsize);
    return paint;
  }

覆盖 onsizechanged() ,得到控件的宽高,并决定要绘制区域的大小(控件默认设置了内边距)

@override
  protected void onsizechanged(int w, int h, int oldw, int oldh) {
    super.onsizechanged(w, h, oldw, oldh);
    viewwidth = w;
    viewheight = h;
    halfview = math.min(viewwidth, viewheight) / 2;  //宽度或高度中最小值的一半,即决定圆心的位置。
    radius = (math.min(viewwidth, viewheight) - 2 * default_padding) / 2; //绘制园的半径是宽高除去默认内边距
  }

核心绘制代码,覆盖ondraw()方法,根据动画是否结束的标志,决定是否绘制分数对应的文本。

@override
  protected void ondraw(canvas canvas) {
    super.ondraw(canvas);
    drawarcbackground(canvas);
    drawarcprogress(canvas);
    drawscoretext(canvas);
    if (isanimend) {
      drawdesctext(canvas);
    }
  }

(1)绘制圆弧背景和灰色刻度背景。

 private void drawarcbackground(canvas canvas) {
    canvas.save();
    canvas.translate(halfview, halfview);
    //绘制圆弧
    rectf rectf = new rectf(dp2px(5) - radius, dp2px(5) - radius, radius - dp2px(5), radius - dp2px(5));
    canvas.drawarc(rectf, 120, 300, false, arcpaint);
    //绘制刻度线
    canvas.rotate(30);
    for (int i = 0; i < 100; i++) {
      canvas.drawline(0, radius - dp2px(15), 0,
          radius - dp2px(15) - linelength,
          bgpaint);
      canvas.rotate(degree);
    }
    canvas.restore();
  }

(2) 绘制刻度,根据valueanimator进行动画的当前值curvalue,来动态改变绘制指示块和已达进度圆弧,从而实现从0开始移动到设定值的动画效果。

private void drawarcprogress(canvas canvas) {
    canvas.save();
    canvas.translate(halfview, halfview);
    //绘制圆弧
    rectf rectf = new rectf(dp2px(5) - radius, dp2px(5) - radius, radius - dp2px(5), radius - dp2px(5));
    canvas.drawarc(rectf, 120, curvalue * degree, false, arcreachpaint);
    //绘制指示方块,方块是从0开始移动某一个位置的
    canvas.rotate(30 + degree * curvalue);
    path path = new path();
    path.moveto(dp2px(5), radius);
    path.lineto(dp2px(5), radius - dp2px(10));
    path.lineto(0, radius - dp2px(15));
    path.lineto(-dp2px(5), radius - dp2px(10));
    path.lineto(-dp2px(5), radius);
    path.close();
    canvas.drawpath(path, arrowpaint);
    //绘制已经达到的刻度
    canvas.restore();
    canvas.save();
    canvas.translate(halfview, halfview);
    canvas.rotate(30);
    for (int i = 0; i < curvalue; i++) {
      canvas.drawline(0, radius - dp2px(15), 0,
          radius - dp2px(15) - linelength,
          reachprogresspaint);
      canvas.rotate(degree);
    }
    canvas.restore();
  }

(3) 绘制分数文本。

private void drawscoretext(canvas canvas) {
    canvas.save();
    canvas.translate(halfview, halfview);
    string scoretext = curvalue + "分";
    float textlength = scoretextpaint.measuretext(scoretext);
    canvas.drawtext(scoretext, -textlength / 2, 0, scoretextpaint);
    canvas.restore();
  }

(4) 动画结束时,绘制最终分数对应的提示信息,该信息只有在动画结束后,才会显示出来。

private void drawdesctext(canvas canvas) {
    canvas.save();
    canvas.translate(halfview, halfview);
    string desc = "";
    if (curvalue >= 90) {
      desc = "车神";
    } else {
      desc = "马路杀手";
    }
    float desclength = desctextpaint.measuretext(desc);
    canvas.drawtext(desc, -desclength / 2, dp2px(30), desctextpaint);
    canvas.restore();
    isanimend = false; // isanimend置为false,防止再次点击start时,就显示动画结束时才能显示的内容
  }

(5)提供对外设置最大值的接口,决定最后的分数。

 /**
   * 对外提供的接口,用于设置进度的最大值
   *
   * @param value
   */
  public void setmaxvalue(int value) {
    if (valueanimator == null) {
      valueanimator = valueanimator.ofint(0, value);
    }
    valueanimator.setinterpolator(new linearinterpolator());
    valueanimator.setduration(30 * value);
    valueanimator.addupdatelistener(new valueanimator.animatorupdatelistener() {
      @override
      public void onanimationupdate(valueanimator animation) {
        curvalue = (int) animation.getanimatedvalue();
        log.i("debug", "curvalue=" + curvalue);
        invalidate();
      }
    });
    valueanimator.addlistener(new animatorlisteneradapter() {
      @override
      public void onanimationend(animator animation) {
        super.onanimationend(animation);
        isanimend = true; //标记动画结束
        log.i("debug", "onanimationend");
        log.i("debug", "onanimationend curvalue = " + curvalue);
        invalidate();
      }
    });
    valueanimator.start();
  }

完整代码:

public class scoreview extends view {
  private final int default_padding = dp2px(5);
  private final int default_width = dp2px(200);  //默认宽度为200dp
  private final int default_height = dp2px(200); //默认高度为200dp
  private int viewwidth; //view宽度
  private int viewheight;  //view高度
  private int halfview; //view宽度或高度的一半
  private int radius;  //绘制圆形区域的半径
  private paint bgpaint;
  private paint arrowpaint; //指示块画笔
  private paint arcpaint; //圆弧画笔
  private paint arcreachpaint; //圆弧画笔
  private paint reachprogresspaint; //已达刻度
  private paint scoretextpaint;  //绘制分数文本
  private paint desctextpaint;  //绘制描述文本
  private float degree = 3f; //相邻刻度之间夹角大小为3度,角度制,不是弧度制
  private float linelength;
  private int linecolor;
  private int curvalue;  //动画进行的当前值
  private valueanimator valueanimator;
  private boolean isanimend = false;
  public scoreview(context context) {
    this(context, null);
  }
  public scoreview(context context, attributeset attrs) {
    super(context, attrs);
    obtainattrs(context, attrs);
    init();
  }
  private void obtainattrs(context context, attributeset attrs) {
    typedarray typedarray = context.obtainstyledattributes(attrs, r.styleable.scoreview);
    linelength = typedarray.getdimension(r.styleable.scoreview_linelength, dp2px(10));
    linecolor = typedarray.getcolor(r.styleable.scoreview_linecolor, color.white);
    typedarray.recycle();
  }
  private void init() {
    arrowpaint = createpaint(color.white, 0, paint.style.fill, 0);
    arcpaint = createpaint(linecolor, dp2px(1), paint.style.stroke, 0);
    bgpaint = createpaint(linecolor, dp2px(1), paint.style.fill, 0);
    reachprogresspaint = createpaint(color.white, dp2px(1), paint.style.fill, 0);
    arcreachpaint = createpaint(color.white, dp2px(1), paint.style.stroke, 0);
    scoretextpaint = createpaint(color.white, 0, paint.style.stroke, dp2px(26));
    desctextpaint = createpaint(color.white, 0, paint.style.stroke, dp2px(16));
  }
  @override
  protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
    super.onmeasure(widthmeasurespec, heightmeasurespec);
    setmeasureddimension(measuresize(widthmeasurespec, default_width), measuresize(heightmeasurespec, default_height));
  }
  private int measuresize(int measurespec, int defaultsize) {
    int measuresize = defaultsize;
    int mode = measurespec.getmode(measurespec);
    int size = measurespec.getsize(measurespec);
    if (mode == measurespec.exactly) {
      measuresize = size;
    } else {
      if (mode == measurespec.at_most) {
        measuresize = math.min(defaultsize, size);
      }
    }
    return measuresize;
  }
  @override
  protected void onsizechanged(int w, int h, int oldw, int oldh) {
    super.onsizechanged(w, h, oldw, oldh);
    viewwidth = w;
    viewheight = h;
    halfview = math.min(viewwidth, viewheight) / 2;  //宽度或高度中最小值的一半,即圆形的位置
    radius = (math.min(viewwidth, viewheight) - 2 * default_padding) / 2; //半径是宽高除去默认内边距的
  }
  @override
  protected void ondraw(canvas canvas) {
    super.ondraw(canvas);
    drawarcbackground(canvas);
    drawarcprogress(canvas);
    drawscoretext(canvas);
    if (isanimend) {
      drawdesctext(canvas);
    }
  }
  private void drawscoretext(canvas canvas) {
    canvas.save();
    canvas.translate(halfview, halfview);
    string scoretext = curvalue + "分";
    float textlength = scoretextpaint.measuretext(scoretext);
    canvas.drawtext(scoretext, -textlength / 2, 0, scoretextpaint);
    canvas.restore();
  }
  /**
   * 绘制文本
   *
   * @param canvas
   */
  private void drawdesctext(canvas canvas) {
    canvas.save();
    canvas.translate(halfview, halfview);
    string desc = "";
    if (curvalue >= 90) {
      desc = "车神";
    } else {
      desc = "马路杀手";
    }
    float desclength = desctextpaint.measuretext(desc);
    canvas.drawtext(desc, -desclength / 2, dp2px(30), desctextpaint);
    canvas.restore();
    isanimend = false; // isanimend置为false,防止再次点击start时,就显示动画结束时才能显示的内容
  }
  /**
   * 绘制进度
   *
   * @param canvas
   */
  private void drawarcprogress(canvas canvas) {
    canvas.save();
    canvas.translate(halfview, halfview);
    //绘制圆弧
    rectf rectf = new rectf(dp2px(5) - radius, dp2px(5) - radius, radius - dp2px(5), radius - dp2px(5));
    canvas.drawarc(rectf, 120, curvalue * degree, false, arcreachpaint);
    //绘制指示方块,方块是从0开始移动某一个位置的
    canvas.rotate(30 + degree * curvalue);
    path path = new path();
    path.moveto(dp2px(5), radius);
    path.lineto(dp2px(5), radius - dp2px(10));
    path.lineto(0, radius - dp2px(15));
    path.lineto(-dp2px(5), radius - dp2px(10));
    path.lineto(-dp2px(5), radius);
    path.close();
    canvas.drawpath(path, arrowpaint);
    //绘制已经达到的刻度
    canvas.restore();
    canvas.save();
    canvas.translate(halfview, halfview);
    canvas.rotate(30);
    for (int i = 0; i < curvalue; i++) {
      canvas.drawline(0, radius - dp2px(15), 0,
          radius - dp2px(15) - linelength,
          reachprogresspaint);
      canvas.rotate(degree);
    }
    canvas.restore();
  }
  /**
   * 绘制背景
   *
   * @param canvas
   */
  private void drawarcbackground(canvas canvas) {
    canvas.save();
    canvas.translate(halfview, halfview);
    //绘制圆弧
    rectf rectf = new rectf(dp2px(5) - radius, dp2px(5) - radius, radius - dp2px(5), radius - dp2px(5));
    canvas.drawarc(rectf, 120, 300, false, arcpaint);
    //绘制刻度线
    canvas.rotate(30);
    for (int i = 0; i < 100; i++) {
      canvas.drawline(0, radius - dp2px(15), 0,
          radius - dp2px(15) - linelength,
          bgpaint);
      canvas.rotate(degree);
    }
    canvas.restore();
  }
  /**
   * 创建画笔
   *
   * @param color
   * @param strokewidth
   * @param style
   * @param textsize
   * @return
   */
  private paint createpaint(int color, int strokewidth, paint.style style, float textsize) {
    paint paint = new paint(paint.anti_alias_flag);
    paint.setcolor(color);
    paint.setstrokewidth(strokewidth);
    paint.setstyle(style);
    paint.setstrokejoin(paint.join.round);
    paint.setstrokecap(paint.cap.round);
    paint.settextsize(textsize);
    return paint;
  }
  /**
   * dp转换成px
   *
   * @param dpvalue
   * @return
   */
  private int dp2px(int dpvalue) {
    return (int) typedvalue.applydimension(typedvalue.complex_unit_dip,
        dpvalue, getresources().getdisplaymetrics());
  }
  /**
   * 对外提供的接口,用于设置进度的最大值
   *
   * @param value
   */
  public void setmaxvalue(int value) {
    if (valueanimator == null) {
      valueanimator = valueanimator.ofint(0, value);
    }
    valueanimator.setinterpolator(new linearinterpolator());
    valueanimator.setduration(30 * value);
    valueanimator.addupdatelistener(new valueanimator.animatorupdatelistener() {
      @override
      public void onanimationupdate(valueanimator animation) {
        curvalue = (int) animation.getanimatedvalue();
        log.i("debug", "curvalue=" + curvalue);
        invalidate();
      }
    });
    valueanimator.addlistener(new animatorlisteneradapter() {
      @override
      public void onanimationend(animator animation) {
        super.onanimationend(animation);
        isanimend = true; //标记动画结束
        log.i("debug", "onanimationend");
        log.i("debug", "onanimationend curvalue = " + curvalue);
        invalidate();
      }
    });
    valueanimator.start();
  }
}

以上所述是小编给大家介绍的android自定义view实现仿驾考宝典显示分数效果(收藏),希望对大家有所帮助

上一篇:

下一篇: