先看下最终的效果
静态:
动态:
一、开始实现
新建一个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软件编程有所帮助。