Android自定义View之酷炫圆环(二)
先看下最终的效果
静态:
动态:
一、开始实现
新建一个doughnutprogress继承view
public class doughnutprogress extends view { }
先给出一些常量、变量以及公共方法的代码,方便理解后面的代码
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方法可以看我的上一篇文章,点这里
/** * 当布局为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的中心作为原点
resetparams(); //将画布中心设为原点(0,0), 方便后面计算坐标 canvas.translate(width / 2, height / 2);
实现静态的渐变圆环
1、画渐变圆环
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、画圆环旋转头部的圆
//画旋转头部圆 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)
让圆环旋转起来
在绘制圆环之前加上下面的代码:
//转起来 canvas.rotate(-currentangle, 0, 0); if (currentangle >= 360f){ currentangle = currentangle - 360f; } else{ currentangle = currentangle + 2f; }
然后再让一个线程循环刷新就好了
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大小继续增大,就实现了类似水波涟漪的效果了。
//实现类似水波涟漪效果 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软件编程有所帮助。