先看下最终的效果
静态:
动态:
一、开始实现
新建一个doughnutprogress继承view
1
2
3
|
public class doughnutprogress extends view { } |
先给出一些常量、变量以及公共方法的代码,方便理解后面的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
private static final int default_min_width = 400 ; //view默认最小宽度 private static final int red = 230 , green = 85 , blue = 35 ; //基础颜色,这里是橙红色 private static final int min_alpha = 30 ; //最小不透明度 private static final int max_alpha = 255 ; //最大不透明度 private static final float doughnutraduispercent = 0 .65f; //圆环外圆半径占view最大半径的百分比 private static final float doughnutwidthpercent = 0 .12f; //圆环宽度占view最大半径的百分比 private static final float middle_wave_raduis_percent = 0 .9f; //第二个圆出现时,第一个圆的半径百分比 private static final float wave_width = 5f; //波纹圆环宽度 //圆环颜色 private static int [] doughnutcolors = new int []{ color.argb(max_alpha, red, green, blue), color.argb(min_alpha, red, green, blue), color.argb(min_alpha, red, green, blue)}; private paint paint = new paint(); //画笔 private float width; //自定义view的宽度 private float height; //自定义view的高度 private float currentangle = 0f; //当前旋转角度 private float raduis; //自定义view的最大半径 private float firstwaveraduis; private float secondwaveraduis; // private void resetparams() { width = getwidth(); height = getheight(); raduis = math.min(width, height)/ 2 ; } private void initpaint() { paint.reset(); paint.setantialias( true ); } |
重写onmeasure方法,为什么要重写onmeasure方法可以看我的上一篇文章,点这里
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/** * 当布局为wrap_content时设置默认长宽 * * @param widthmeasurespec * @param heightmeasurespec */ @override protected void onmeasure( int widthmeasurespec, int heightmeasurespec) { setmeasureddimension(measure(widthmeasurespec), measure(heightmeasurespec)); } private int measure( int origin) { int result = default_min_width; int specmode = measurespec.getmode(origin); int specsize = measurespec.getsize(origin); if (specmode == measurespec.exactly) { result = specsize; } else { if (specmode == measurespec.at_most) { result = math.min(result, specsize); } } return result; } |
下面就是最重要的重写ondraw方法,大致流程如下
在开始绘制之前,先初始化width、height、raduis, 以及将view的中心作为原点
1
2
3
4
|
resetparams(); //将画布中心设为原点(0,0), 方便后面计算坐标 canvas.translate(width / 2 , height / 2 ); |
实现静态的渐变圆环
1、画渐变圆环
1
2
3
4
5
6
7
8
9
10
11
12
|
float doughnutwidth = raduis * doughnutwidthpercent; //圆环宽度 //圆环外接矩形 rectf rectf = new rectf( -raduis * doughnutraduispercent, -raduis * doughnutraduispercent, raduis * doughnutraduispercent, raduis * doughnutraduispercent); initpaint(); paint.setstrokewidth(doughnutwidth); paint.setstyle(paint.style.stroke); paint.setshader( new sweepgradient( 0 , 0 , doughnutcolors, null )); canvas.drawarc(rectf, 0 , 360 , false , paint); |
通过修改doughnutcolors可以实现不同的渐变效果
2、画圆环旋转头部的圆
1
2
3
4
5
|
//画旋转头部圆 initpaint(); paint.setstyle(paint.style.fill); paint.setcolor(color.argb(max_alpha, red, green, blue)); canvas.drawcircle(raduis * doughnutraduispercent, 0 , doughnutwidth / 2 , paint); |
此时运行代码得到效果如下图:
我们还可以在绘制圆环之前通过旋转画布得到不同初始状态
canvas.rotate(-45, 0, 0);
canvas.rotate(-180, 0, 0);
此时聪明的你应该已经想到怎么让这个圆环旋转起来了吧^_^
对!正如你所想的,就是通过canvas.rotate方法不停地旋转画布(这个“地”是这么用的吧o(╯□╰)o)
让圆环旋转起来
在绘制圆环之前加上下面的代码:
1
2
3
4
5
6
7
|
//转起来 canvas.rotate(-currentangle, 0 , 0 ); if (currentangle >= 360f){ currentangle = currentangle - 360f; } else { currentangle = currentangle + 2f; } |
然后再让一个线程循环刷新就好了
1
2
3
4
5
6
7
8
9
10
11
12
13
|
private thread thread = new thread(){ @override public void run() { while ( true ){ try { thread.sleep( 10 ); } catch (interruptedexception e) { e.printstacktrace(); } postinvalidate(); } } }; |
试试!转起来了吗o(∩_∩)o~
下面是比较有意思的部分,实现类似水波涟漪的效果
分析水波涟漪效果的实现原理(画了张草图方便理解):
假设淡黄色背景区域为整个view的大小
黑色圆圈为view内的最大圆(半径为r3)
橙色圆环代表渐变圆环
红色圆圈代表圆环的外圆(半径为r1)
紫色圆圈是干啥子的,待会儿再介绍~(半径为r2)
通过观察实现的最终效果,可以发现有个圆的半径从r1逐渐增大r3,不透明度逐渐减小到0。
那是不是这样周而复始就可以实现最终的效果了呢?
没那么简单。。。
仔细观察发现,第二个圆不是等到第一个圆的半径增大到r3才开始出现的,而是在将要消失的时候就出现了,有一段时间是两个圆同时存在的。
那么我们就假设当第一个圆的半径增大到r2,第二个圆开始出现。
开始想象两个圆的循环运行模型~~~
我的方案是:
绘制两个圆,每个圆的半径都从r1增大到r1+2x(r2-r1),不透明度还是从r1到r3的过程中逐渐变为0,也就是当圆的半径大于r3时,不透明度就为0了(不可见了),将第一个圆半径初始值设为r1,第二个圆半径初始值设为r2。这样两个圆半径同时逐渐增大,当半径大于 r1+2x(r2-r1)时又重新回到r1大小继续增大,就实现了类似水波涟漪的效果了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
//实现类似水波涟漪效果 initpaint(); paint.setstyle(paint.style.stroke); paint.setstrokewidth( 5 ); secondwaveraduis = calculatewaveraduis(secondwaveraduis); firstwaveraduis = calculatewaveraduis(secondwaveraduis + raduis*(middle_wave_raduis_percent - doughnutraduispercent) - raduis*doughnutwidthpercent/ 2 ); paint.setcolor(color.argb(calculatewavealpha(secondwaveraduis), red, green, blue)); canvas.drawcircle( 0 , 0 , secondwaveraduis, paint); //画第二个圆(初始半径较小的) initpaint(); paint.setstyle(paint.style.stroke); paint.setstrokewidth( 5 ); paint.setcolor(color.argb(calculatewavealpha(firstwaveraduis), red, green, blue)); canvas.drawcircle( 0 , 0 , firstwaveraduis, paint); //画第一个圆(初始半径较大的) /** * 计算波纹圆的半径 * @param waveraduis * @return */ private float calculatewaveraduis( float waveraduis){ if (waveraduis < raduis*doughnutraduispercent + raduis*doughnutwidthpercent/ 2 ){ waveraduis = raduis*doughnutraduispercent + raduis*doughnutwidthpercent/ 2 ; } if (waveraduis > raduis*middle_wave_raduis_percent + raduis*(middle_wave_raduis_percent - doughnutraduispercent) - raduis*doughnutwidthpercent/ 2 ){ waveraduis = waveraduis - (raduis*middle_wave_raduis_percent + raduis*(middle_wave_raduis_percent - doughnutraduispercent) - raduis*doughnutwidthpercent/ 2 ) + raduis*doughnutwidthpercent/ 2 + raduis*doughnutraduispercent; } waveraduis += 0 .6f; return waveraduis; } /** * 根据波纹圆的半径计算不透明度 * @param waveraduis * @return */ private int calculatewavealpha( float waveraduis){ float percent = (waveraduis-raduis*doughnutraduispercent-raduis*doughnutwidthpercent/ 2 )/(raduis-raduis*doughnutraduispercent-raduis*doughnutwidthpercent/ 2 ); if (percent >= 1f){ return 0 ; } else { return ( int ) (min_alpha*(1f-percent)); } } |
以上就是本文的全部内容,希望对大家的学习android软件编程有所帮助。