Android自定义控件实现带数值和动画的圆形进度条
程序员文章站
2022-08-20 16:09:34
本文实例实现一个如下图所示的android自定义控件,可以直观地展示某个球队在某个赛季的积分数和胜场、负场、平局数
首先对画布进行区域划分,整个控件分上下两部分
上...
本文实例实现一个如下图所示的android自定义控件,可以直观地展示某个球队在某个赛季的积分数和胜场、负场、平局数
首先对画布进行区域划分,整个控件分上下两部分
上边是个大的圆环,圆环中间两行文字,没什么难度,选好圆心坐标和半径后直接绘制即可,绘制文字也是如此。
下部分是三个小的圆弧进度条,弧的末端绘制一个小的实心圆
首先选好坐标和半径,然后先绘制三个圆环作为弧形进度条的背景
之后从12点钟开始绘制进度弧,知道了圆环的圆心和半径,也知道了弧对应于12点钟和圆环圆心的偏移角度
通过三角函数可以计算出进度弧终点坐标,以进度弧终点坐标为圆心绘制一个小的实心圆即可
动画效果通过handler的postdelayed方法触发重绘即可实现
在项目中的效果如图所示:
测试代码如下:
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(); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。