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" />

java代码中使用样例
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);
	}

如何实现自定义组件

关键是在viewOnDraw方法中进行重绘。

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!




郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。