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

Android自定义控件实现带数值和动画的圆形进度条

程序员文章站 2022-08-20 16:09:34
本文实例实现一个如下图所示的android自定义控件,可以直观地展示某个球队在某个赛季的积分数和胜场、负场、平局数 首先对画布进行区域划分,整个控件分上下两部分 上...

本文实例实现一个如下图所示的android自定义控件,可以直观地展示某个球队在某个赛季的积分数和胜场、负场、平局数

Android自定义控件实现带数值和动画的圆形进度条

首先对画布进行区域划分,整个控件分上下两部分

上边是个大的圆环,圆环中间两行文字,没什么难度,选好圆心坐标和半径后直接绘制即可,绘制文字也是如此。

下部分是三个小的圆弧进度条,弧的末端绘制一个小的实心圆

首先选好坐标和半径,然后先绘制三个圆环作为弧形进度条的背景

之后从12点钟开始绘制进度弧,知道了圆环的圆心和半径,也知道了弧对应于12点钟和圆环圆心的偏移角度

通过三角函数可以计算出进度弧终点坐标,以进度弧终点坐标为圆心绘制一个小的实心圆即可

动画效果通过handler的postdelayed方法触发重绘即可实现

在项目中的效果如图所示:

Android自定义控件实现带数值和动画的圆形进度条

测试代码如下:

final random random=new random();
final scoreboardview myview=(scoreboardview)findviewbyid(r.id.custom_view);
myview.setonclicklistener(new view.onclicklistener(){
 @override
 public void onclick(view v){
  myview.setcolor(color.blue);
  myview.setscore(random.nextint(28));
  myview.setwindrawlose(random.nextint(12),random.nextint(15),random.nextint(26));
 }
});

完整代码如下:

public class scoreboardview extends view {
 private context context;
 /*弧的颜色*/
 private int mcolor;
 /*积分数,胜场数,平局数,负场数*/
 private int mscore, mwinnumber, mdrawnumber, mlosenumber;
 /*传入数字的最大值*/
 private final int full_score = 30;
 /*动画插值器*/
 decelerateinterpolator mdecelerateinterpolator = new decelerateinterpolator();
 /*动画持续时间(刷新次数)*/
 private int mduration = 10;
 /*动画刷新过程中的当前值*/
 private int mcurrenttime = 0;
 private typedvalue typedvalue;
 private typedvalue typedvalue1;
 private handler mhandler = new handler();
 private runnable manimation = new runnable() {
  @override
  public void run() {
   if (mcurrenttime < mduration) {
    mcurrenttime++;
    /*导致重绘调用ondraw,ondraw最后再次postdelay执行此动画,直到达到指定的次数*/
    scoreboardview.this.invalidate();
   }
  }
 };
 /*绘制图形*/
 private paint paint = new paint();
 /*绘制文字*/
 private paint painttext = new paint();
 
 public scoreboardview(context context) {
  super(context);
  this.context=context;
  init();
 }
 
 public scoreboardview(context context, attributeset attrs) {
  super(context, attrs);
  this.context=context;
  init();
 }
 
 public scoreboardview(context context, attributeset attrs, int defstyleattr) {
  super(context, attrs, defstyleattr);
  this.context=context;
  init();
 }
 
 private void init() {
  /*数据初始化,默认属性*/
  mcolor = color.rgb(95, 112, 72);
  mscore = 0;
  mwinnumber = 0;
  mdrawnumber = 0;
  mlosenumber = 0;
  typedvalue=new typedvalue();
  typedvalue1=new typedvalue();
  context.gettheme().resolveattribute(r.attr.maintextclor, typedvalue, true);
  context.gettheme().resolveattribute(r.attr.maintextclor_reverse,typedvalue1,true);
 }
 
 @override
 protected void ondraw(canvas canvas) {
  super.ondraw(canvas);
  /*获取控件总的宽高*/
  float totalwidth = getwidth();
  float totalheight = getheight();
  /*
  * decelerateinterpolator:动画从开始到结束,变化率是一个减速的过程。
  * accelerateinterpolator:动画从开始到结束,变化率是一个加速的过程。
  * cycleinterpolator:动画从开始到结束,变化率是循环给定次数的正弦曲线
  * acceleratedecelerateinterpolator:动画从开始到结束,变化率是先加速后减速的过程。
  * linearinterpolator:动画从开始到结束,变化率是线性变化。
  * */
  /*计算当前时刻动画进度值*/
  float animcurrentvalue =mdecelerateinterpolator.getinterpolation(1.0f * mcurrenttime / mduration);
 
  /*图形画笔设置*/
  paint.setantialias(true);
  paint.setstyle(paint.style.stroke);
 
  /*积分数,上边的大圆*/
  paint.setstrokewidth(4);
  paint.setcolor(mcolor);
  /*积分大圆的中心坐标和半径*/
  float score_radius = totalheight * 1 / 5, score_circle_x = totalwidth / 2, score_circle_y = totalheight / 3;
  /*绘制圆弧*/
  canvas.drawcircle(score_circle_x, score_circle_y, score_radius, paint);
  /*文字画笔基本设置*/
  painttext.setantialias(true);
  painttext.setstyle(paint.style.stroke);
  /*文字从中间开始绘制*/
  /*paint.align.center:the text is drawn centered horizontally on the x,y origin*/
  painttext.settextalign(paint.align.center);
  /*文字画笔大小和颜色设置*/
  painttext.settextsize(score_radius * 3 / 4);
  painttext.setcolor(getresources().getcolor(typedvalue.resourceid));
  /*圆心位置绘制积分数值*/
  canvas.drawtext("" + mscore, score_circle_x, score_circle_y, painttext);
  /*缩小字体绘制文本信息*/
  painttext.settextsize(score_radius * 1 / 4);
  painttext.setalpha(80);
  /*圆心下边绘制文本*/
  canvas.drawtext("积分", score_circle_x, score_circle_y + score_radius / 2, painttext);
 
  /*设置画笔,画下边的三个小圆*/
  paint.setstrokewidth(4);
  paint.setalpha(255);
  /*下边三个小圆的半径*/
  float small_radius = totalheight / 10;
  /*三个小圆的圆心的x坐标*/
  float[] circlexs = new float[]{totalwidth / 2 - score_radius * 3 / 2,
          totalwidth / 2,
          totalwidth / 2 + score_radius * 3 / 2};
  /*三个小圆的圆心的y坐标*/
  float circley = totalheight * 3 / 4;
  /*计算三个小圆弧扫过的角度*/
  float[] theta_values = new float[]{360 * mwinnumber / full_score* animcurrentvalue,
           360 * mdrawnumber / full_score* animcurrentvalue,
           360 * mlosenumber / full_score* animcurrentvalue};
  /*设置画笔颜色,绘制外围圆环*/
  paint.setcolor(getresources().getcolor(typedvalue1.resourceid));
  /*分别绘制三个外围圆环*/
  canvas.drawcircle(circlexs[0], circley, small_radius, paint);//画win背景圆
  canvas.drawcircle(circlexs[1], circley, small_radius, paint);//画draw背景圆
  canvas.drawcircle(circlexs[2], circley, small_radius, paint);//画lose背景圆
  /*更改画笔颜色,绘制圆弧进度条*/
  paint.setcolor(mcolor);
  /*drawarc的起始角度是3点钟方向,因此要从12点钟方向开始绘制,起始角度为270度*/
  canvas.drawarc(new rectf(circlexs[0] - small_radius,
         circley - small_radius,
         circlexs[0] + small_radius,
         circley + small_radius),
      270, theta_values[0], false, paint);//画win圆形进度条
  canvas.drawarc(new rectf(circlexs[1] - small_radius,
         circley - small_radius,
         circlexs[1] + small_radius,
         circley + small_radius),
      270, theta_values[1], false, paint);//画draw圆形进度条
  canvas.drawarc(new rectf(circlexs[2] - small_radius,
         circley - small_radius,
         circlexs[2] + small_radius,
         circley + small_radius),
      270, theta_values[2], false, paint);//画lose圆形进度条
  /*绘制圆弧结束处的小圆点,实心圆*/
  paint.setstyle(paint.style.fill);
  /*已知半径、圆心位置、便宜角度,根据三角函数很方便计算出小实心圆圆心坐标*/
  canvas.drawcircle(circlexs[0] + small_radius * (float) math.sin(theta_values[0] * math.pi / 180),
    circley - small_radius * (float) math.cos(theta_values[0] * math.pi / 180), 6, paint);//画win末尾小圆点
  canvas.drawcircle(circlexs[1] + small_radius * (float) math.sin(theta_values[1] * math.pi / 180),
    circley - small_radius * (float) math.cos(theta_values[1] * math.pi / 180), 6, paint);//画draw末尾小圆点
  canvas.drawcircle(circlexs[2] + small_radius * (float) math.sin(theta_values[2] * math.pi / 180),
    circley - small_radius * (float) math.cos(theta_values[2] * math.pi / 180), 6, paint);//画lose末尾小圆点
 
  /*绘制文字*/
  painttext.setcolor(getresources().getcolor(typedvalue.resourceid));
  painttext.settextsize(small_radius * 2 / 3);
  /*测量文字大小,确定绘制文字时垂直方向的位置*/
  paint.fontmetrics fontmetrics = paint.getfontmetrics();
  float textbaselineoffset = (fontmetrics.bottom - fontmetrics.top) / 2 - fontmetrics.bottom;
  /*在三个小圆的正中心位置绘制相应的数字*/
  canvas.drawtext("" + (int)(mwinnumber * animcurrentvalue), circlexs[0], circley + textbaselineoffset, painttext);
  canvas.drawtext("" + (int)(mdrawnumber * animcurrentvalue), circlexs[1], circley + textbaselineoffset, painttext);
  canvas.drawtext("" + (int)(mlosenumber * animcurrentvalue), circlexs[2], circley + textbaselineoffset, painttext);
  /*调整字体大小,绘制文本信息*/
  painttext.settextsize(small_radius * 4 / 9);
  canvas.drawtext("胜场", circlexs[0], circley - small_radius*4/3, painttext);
  canvas.drawtext("平局", circlexs[1], circley - small_radius*4/3, painttext);
  canvas.drawtext("负场", circlexs[2], circley - small_radius*4/3, painttext);
  /*20ms刷新一次数据*/
  mhandler.postdelayed(manimation, 20);//启动动画
 }
 
 public void setcolor(int mcolor) {
  this.mcolor = mcolor;
  invalidate();
 }
 
 public void setscore(int score) {
  this.mscore = score;
  invalidate();
 }
 
 public void setwindrawlose(int win,int draw,int lose) {
  this.mwinnumber = win;
  this.mdrawnumber = draw;
  this.mlosenumber = lose;
  mcurrenttime =0;
  invalidate();
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。