Android自定义控件NumberCircleProgressBar(圆形进度条)的实现
最近在Github上看到了daimajia写的一个开源组件NumberProgressBar觉得非常好,故而在其基础上进行了一些延伸与扩展,编写了一个NumberCircleProgressBar(即圆形的进度条),并且分为两种模式,我称之为rotate模式和rising_water模式。
PS:也许本文介绍的方法不是最优(比如游戏开发中可能只需要调用一个方法即可完成),也可能会有纰漏,所以请读者海涵!
NumberCircleProgressBar的样图如下:
Rotate模式
Rising_Water模式
组件属性
对于两种不同的模式,如上图所示,均有以下属性:
TheCircle
- circle_radius
- fill_mode(name="rotate" value="0",name="rising_water" value="1" )
The reached area and unreachedarea:
- color
- height
The text area:
- color
- text size
- visibility
The bar:
- max progress
- current progress
如何使用
这个组件很小,就没必要制作成库了,所以使用者copy一下相关目录下文件即可:
1. 将src/NumberCircleProgressBar.java文件拷贝到相应目录;
2. 将res/values/attrs.xml中NumberCircleProgressBar的属性定义拷贝到相应目录;
在布局文件中定义NumberCircleProgressBar
<com.cjl.numbercircleprogressbar.NumberCircleProgressBar android:id="@+id/numbercircleprogress_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:padding="10dp" custom:max="100" custom:progress="0" custom:progress_circle_radius="50dp" custom:progress_fill_mode="rotate" custom:progress_reached_color="#3498DB" custom:progress_text_color="@android:color/black" custom:progress_text_size="15sp" custom:progress_text_visibility="visible" custom:progress_unreached_color="#CCCCCC" />
在style.xml中定义NumberCircleProgressBar属性
<style name="NumberCircleProgressBar_Default"> <item name="android:layout_height">wrap_content</item> <item name="android:layout_width">match_parent</item> <item name="max">100</item> <item name="progress">10</item> <item name="progress_unreached_color">#CCCCCC</item> <item name="progress_reached_color">#3498DB</item> <item name="progress_circle_radius">20dp</item> <item name="progress_text_size">10sp</item> <item name="progress_text_color">#000000</item> <item name="progress_fill_mode">0</item> </style>
在布局文件中使用style
<com.cjl.numbercircleprogressbar.NumberCircleProgressBar style="@style/NumberCircleProgressBar_Default" android:layout_width="wrap_content" android:layout_height="wrap_content" anroid:padding="10dp" />
public void setTheNumberProgressBar() { final NumberCircleProgressBar bnp = (NumberCircleProgressBar) findViewById(R.id.numbercircleprogress_bar); timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { if (!isFinish) { bnp.incrementProgressBy(2); if (bnp.isFinished()) { isFinish = false; } } } }); } }, 1000, 100); }
如何实现自定义组件
关键是在view的OnDraw方法中进行重绘。
Mode Rotate 实现方式
1.使用unreachedarea的颜色绘制Circle,作为底层;
canvas.drawCircle(centerX, centerY,mCircleRadius, mCirclePaint);
2.使用reacherarea的颜色绘制扇形,作为中间层;
canvas.drawArc(mCircleRectF,DEFAULT_INITIAL_ANGLE, getProgress() * 360 / getMax(),true, mSectorPaint);
3.使用textarea的颜色绘制文字,作为顶层;
canvas.drawText(mCurrentDrawText,mDrawTextStart, mDrawTextEnd, mTextPaint);
Mode Rising Water实现方法
这里必须要说明一下如何绘制扇形的截取部分,请看下图:
为了绘制图中的蓝色部分,我们需要先绘制蓝色部分对应的扇形,其次再绘制三角形灰色部分覆盖蓝色扇形部分(或者绘制蓝色三角形补全扇形部分)。所以我们必须要知道角α,进而求出三角形中除圆心坐标外的两点。
如何求α?
设p为已知的百分比,则p∈[0,1],
联立方程可以得到
得到方程
这里遇到了难题,如何求超越方程的解?
一开始我在这卡住了,在网上搜了半天,实在没找到这类超越方程的解法。后来突然想到模拟求解啊,这不就是计算机思维的解决之道吗?于是乎下载Mathematic软件来进行求解:
得到了p为[0%,100%]之间的步长为1%的对应角度α的数组:
PERCENT_TO_ARC = { 0, 0.364413d, 0.4616d,
0.530831d, 0.586699d, 0.634474d, 0.676734d, 0.714958d, 0.750081d,
0.782736d, 0.813377d, 0.842337d, 0.869872d, 0.896184d, 0.921432d,
0.945747d, 0.969237d, 0.991993d, 1.01409d, 1.0356d, 1.05657d,
1.07706d, 1.0971d, 1.11674d, 1.13601d, 1.15494d, 1.17356d,
1.19189d, 1.20996d, 1.22779d, 1.24539d, 1.26279d, 1.27999d,
1.29702d, 1.31389d, 1.33061d, 1.3472d, 1.36366d, 1.38d, 1.39625d,
1.4124d, 1.42847d, 1.44446d, 1.46039d, 1.47627d, 1.49209d,
1.50788d, 1.52364d, 1.53937d, 1.55509d, 0.5 * Math.PI, 1.58651d,
1.60222d, 1.61796d, 1.63371d, 1.6495d, 1.66533d, 1.6812d, 1.69713d,
1.71313d, 1.72919d, 1.74535d, 1.76159d, 1.77794d, 1.7944d,
1.81098d, 1.8277d, 1.84457d, 1.8616d, 1.8788d, 1.8962d, 1.9138d,
1.93163d, 1.9497d, 1.96803d, 1.98665d, 2.00558d, 2.02485d,
2.04449d, 2.06454d, 2.08502d, 2.10599d, 2.1275d, 2.1496d, 2.17236d,
2.19585d, 2.22016d, 2.24541d, 2.27172d, 2.29926d, 2.32822d,
2.35886d, 2.39151d, 2.42663d, 2.46486d, 2.50712d, 2.55489d,
2.61076d, 2.67999d, 2.77718d, Math.PI };
得到了角度α,那么成功近在咫尺了:
1. 使用unreachedarea的颜色绘制Circle,作为底层;
2. 使用reacherarea的颜色绘制扇形,作为中间层;
3. 当percent∈[0,50%]时,使用unreached area的颜色绘制三角形覆盖扇形相应位置;
当percent∈[50%,100%]时,使用reached area的颜色绘制三角形填充扇形相应位置;
1. 使用textarea的颜色绘制文字,作为顶层;
大功告成,但此时观察Demo程序,发现还是有点瑕疵。
三角形没有完全覆盖扇形的那部分面积,留下了两条边线隐约可以看到底层扇形的颜色,而且在使用Visio绘制文档中图例时也出现了这种情况!!
查找了半天,实在没发现哪有什么错误的地方。最终只得使用一点实际的办法了——以一定粗度的实线绘制三角形的两条边,覆盖透出的颜色。这也许不是正确的方案,但解决了问题,如果读者有更好的办法或者知道此bug的出处,希望告知!
这里要赞一下腾讯的应用宝,手机没有root,无法使用截屏软件来制作gif,而应用宝里有动态截屏制作gif,有点不足的地方就是需要手动点,如果有设帧率的地方就更好了!
It’s Over!
Mode Rotate 实现方式
1.使用unreachedarea的颜色绘制Circle,作为底层;
canvas.drawCircle(centerX, centerY,mCircleRadius, mCirclePaint);
2.使用reacherarea的颜色绘制扇形,作为中间层;
canvas.drawArc(mCircleRectF,DEFAULT_INITIAL_ANGLE, getProgress() * 360 / getMax(),true, mSectorPaint);
3.使用textarea的颜色绘制文字,作为顶层;
canvas.drawText(mCurrentDrawText,mDrawTextStart, mDrawTextEnd, mTextPaint);
Mode Rising Water实现方法
这里必须要说明一下如何绘制扇形的截取部分,请看下图:
为了绘制图中的蓝色部分,我们需要先绘制蓝色部分对应的扇形,其次再绘制三角形灰色部分覆盖蓝色扇形部分(或者绘制蓝色三角形补全扇形部分)。所以我们必须要知道角α,进而求出三角形中除圆心坐标外的两点。
如何求α?
设p为已知的百分比,则p∈[0,1],
联立方程可以得到
得到方程
这里遇到了难题,如何求超越方程的解?
一开始我在这卡住了,在网上搜了半天,实在没找到这类超越方程的解法。后来突然想到模拟求解啊,这不就是计算机思维的解决之道吗?于是乎下载Mathematic软件来进行求解:
得到了p为[0%,100%]之间的步长为1%的对应角度α的数组:
PERCENT_TO_ARC = { 0, 0.364413d, 0.4616d, 0.530831d, 0.586699d, 0.634474d, 0.676734d, 0.714958d, 0.750081d, 0.782736d, 0.813377d, 0.842337d, 0.869872d, 0.896184d, 0.921432d, 0.945747d, 0.969237d, 0.991993d, 1.01409d, 1.0356d, 1.05657d, 1.07706d, 1.0971d, 1.11674d, 1.13601d, 1.15494d, 1.17356d, 1.19189d, 1.20996d, 1.22779d, 1.24539d, 1.26279d, 1.27999d, 1.29702d, 1.31389d, 1.33061d, 1.3472d, 1.36366d, 1.38d, 1.39625d, 1.4124d, 1.42847d, 1.44446d, 1.46039d, 1.47627d, 1.49209d, 1.50788d, 1.52364d, 1.53937d, 1.55509d, 0.5 * Math.PI, 1.58651d, 1.60222d, 1.61796d, 1.63371d, 1.6495d, 1.66533d, 1.6812d, 1.69713d, 1.71313d, 1.72919d, 1.74535d, 1.76159d, 1.77794d, 1.7944d, 1.81098d, 1.8277d, 1.84457d, 1.8616d, 1.8788d, 1.8962d, 1.9138d, 1.93163d, 1.9497d, 1.96803d, 1.98665d, 2.00558d, 2.02485d, 2.04449d, 2.06454d, 2.08502d, 2.10599d, 2.1275d, 2.1496d, 2.17236d, 2.19585d, 2.22016d, 2.24541d, 2.27172d, 2.29926d, 2.32822d, 2.35886d, 2.39151d, 2.42663d, 2.46486d, 2.50712d, 2.55489d, 2.61076d, 2.67999d, 2.77718d, Math.PI };
得到了角度α,那么成功近在咫尺了:
1. 使用unreachedarea的颜色绘制Circle,作为底层;
2. 使用reacherarea的颜色绘制扇形,作为中间层;
3. 当percent∈[0,50%]时,使用unreached area的颜色绘制三角形覆盖扇形相应位置;
当percent∈[50%,100%]时,使用reached area的颜色绘制三角形填充扇形相应位置;
1. 使用textarea的颜色绘制文字,作为顶层;
大功告成,但此时观察Demo程序,发现还是有点瑕疵。
三角形没有完全覆盖扇形的那部分面积,留下了两条边线隐约可以看到底层扇形的颜色,而且在使用Visio绘制文档中图例时也出现了这种情况!!
查找了半天,实在没发现哪有什么错误的地方。最终只得使用一点实际的办法了——以一定粗度的实线绘制三角形的两条边,覆盖透出的颜色。这也许不是正确的方案,但解决了问题,如果读者有更好的办法或者知道此bug的出处,希望告知!
这里要赞一下腾讯的应用宝,手机没有root,无法使用截屏软件来制作gif,而应用宝里有动态截屏制作gif,有点不足的地方就是需要手动点,如果有设帧率的地方就更好了!
It’s Over!
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。