当前位置: 移动技术网 > IT编程>移动开发>Android > Android特效之水波纹的实现

Android特效之水波纹的实现

2019年07月24日  | 移动技术网IT编程  | 我要评论

智能电视应用,中成药大全,大学生了没佳琪

前言

水波纹特效,想必大家或多或少见过,在我的印象中,大致有如下几种:

     支付宝 "咻咻咻" 式

     流量球 "荡漾" 式

     真实的水波纹效果,基于bitmap处理式

话不多说,先来看看效果:

填充式水波纹,间距相等

非填充式水波纹,间距相等

非填充式水波纹,间距不断变大

填充式水波纹,间距不断变小

想必大家已经知道基本的原理了,就是用canvas来画嘛,但可不是简单的画哦,请往下看。

分析

这种类型的水波纹,其实无非就是画圆而已,在给定的矩形中,一个个圆由最小半径扩大到最大半径,伴随着透明度从1.0变为0.0。我们假定这种扩散是匀速的,则一个圆从创建(透明度为1.0)到消失(透明度为0.0)的时长就是定值,那么某一时刻某一个圆的半径以及透明度完全可以由扩散时间(当前时间 - 创建时间)决定。

实现

按照上面的分析,我们写出以下circle类来表示一个圆:

private class circle {
 private long mcreatetime;

 public circle() {
 this.mcreatetime = system.currenttimemillis();
 }

 public int getalpha() {
 float percent = (system.currenttimemillis() - mcreatetime) * 1.0f / mduration;
 return (int) ((1.0f - percent) * 255);
 }

 public float getcurrentradius() {
 float percent = (system.currenttimemillis() - mcreatetime) * 1.0f / mduration;
 return minitialradius + percent * (mmaxradius - minitialradius);
 }
}

自然而然,在waveview中,要有一个list保存当前正在显示的圆:

private list<circle> mcirclelist = new arraylist<circle>();

我们定义一个start方法,用来启动扩散:

public void start() {
 if (!misrunning) {
 misrunning = true;
 mcreatecircle.run();
 }
}

private runnable mcreatecircle = new runnable() {
 @override
 public void run() {
 if (misrunning) {
 newcircle();
 postdelayed(mcreatecircle, mspeed); // 每隔mspeed毫秒创建一个圆
 }
 }
};

private void newcircle() {
 long currenttime = system.currenttimemillis();
 if (currenttime - mlastcreatetime < mspeed) {
 return;
 }
 circle circle = new circle();
 mcirclelist.add(circle);
 invalidate();
 mlastcreatetime = currenttime;
}

start方法只是简单的创建了一个圆并添加到了mcirclelist中,同时开启了循环创建圆的runnable,然后通知界面刷新,我们再看看ondraw方法:

protected void ondraw(canvas canvas) {
 iterator<circle> iterator = mcirclelist.iterator();
 while (iterator.hasnext()) {
 circle circle = iterator.next();
 if (system.currenttimemillis() - circle.mcreatetime < mduration) {
 mpaint.setalpha(circle.getalpha());
 canvas.drawcircle(getwidth() / 2, getheight() / 2, circle.getcurrentradius(), mpaint);
 } else {
 iterator.remove();
 }
 }
 if (mcirclelist.size() > 0) {
 postinvalidatedelayed(10);
 }
}

ondraw方法遍历了每一个circle,判断circle的扩散时间是否超过了设定的扩散时间,如果是则移除,如果不是,则计算circle当前的透明度和半径并绘制出来。我们添加了一个延时刷新来不断重绘界面,以达到连续的波纹扩散效果。

现在运行程序,应该能看到图2中的效果了,不过有点别扭,按常识,水波的间距是越来越大的,如何做到呢?

技巧

要让水波纹的半径非匀速变大,我们只能去修改circle.getcurrentradius()方法了。我们再次看看这个方法:

public float getcurrentradius() {
 float percent = (system.currenttimemillis() - mcreatetime) * 1.0f / mduration;
 return minitialradius + percent * (mmaxradius - minitialradius);
}

percent表示circle当前扩散时间和总扩散时间的一个百分比,考虑到当前扩散时间超过总扩散时间时circle会被移除,因此percent的实际区间为[0, 1],看到[0, 1],我不知道大家想到的是什么,我首先想到的就是差值器(interpolator),我们可以通过定义差值器来实现对circle半径变化的控制!

我们修改代码:

private interpolator minterpolator = new linearinterpolator();

public void setinterpolator(interpolator interpolator) {
 minterpolator = interpolator;
 if (minterpolator == null) {
 minterpolator = new linearinterpolator();
 }
}

private class circle {
 private long mcreatetime;

 public circle() {
 this.mcreatetime = system.currenttimemillis();
 }

 public int getalpha() {
 float percent = (system.currenttimemillis() - mcreatetime) * 1.0f / mduration;
 return (int) ((1.0f - minterpolator.getinterpolation(percent)) * 255);
 }

 public float getcurrentradius() {
 float percent = (system.currenttimemillis() - mcreatetime) * 1.0f / mduration;
 return minitialradius + minterpolator.getinterpolation(percent) * (mmaxradius - minitialradius);
 }
}

这样,外部使用waveview时,只需调用setinterpolator()来定义不同的插值器即可实现不同的效果。

图3效果的代码:

mwaveview = (waveview) findviewbyid(r.id.wave_view);
mwaveview.setduration(5000);
mwaveview.setstyle(paint.style.stroke);
mwaveview.setspeed(400);
mwaveview.setcolor(color.parsecolor("#ff0000"));
mwaveview.setinterpolator(new accelerateinterpolator(1.2f));
mwaveview.start();

图4效果的代码:

mwaveview = (waveview) findviewbyid(r.id.wave_view);
mwaveview.setduration(5000);
mwaveview.setstyle(paint.style.fill);
mwaveview.setcolor(color.parsecolor("#ff0000"));
mwaveview.setinterpolator(new linearoutslowininterpolator());
mwaveview.start();

附上waveview的所有代码:

/**
 * 水波纹特效
 * created by hackware on 2016/6/17.
 */
public class waveview extends view {
 private float minitialradius; // 初始波纹半径
 private float mmaxradiusrate = 0.85f; // 如果没有设置mmaxradius,可mmaxradius = 最小长度 * mmaxradiusrate;
 private float mmaxradius; // 最大波纹半径
 private long mduration = 2000; // 一个波纹从创建到消失的持续时间
 private int mspeed = 500; // 波纹的创建速度,每500ms创建一个
 private interpolator minterpolator = new linearinterpolator();

 private list<circle> mcirclelist = new arraylist<circle>();
 private boolean misrunning;

 private boolean mmaxradiusset;

 private paint mpaint;
 private long mlastcreatetime;

 private runnable mcreatecircle = new runnable() {
 @override
 public void run() {
 if (misrunning) {
 newcircle();
 postdelayed(mcreatecircle, mspeed);
 }
 }
 };

 public waveview(context context) {
 this(context, null);
 }

 public waveview(context context, attributeset attrs) {
 super(context, attrs);
 mpaint = new paint(paint.anti_alias_flag);
 setstyle(paint.style.fill);
 }

 public void setstyle(paint.style style) {
 mpaint.setstyle(style);
 }

 @override
 protected void onsizechanged(int w, int h, int oldw, int oldh) {
 if (!mmaxradiusset) {
 mmaxradius = math.min(w, h) * mmaxradiusrate / 2.0f;
 }
 }

 public void setmaxradiusrate(float maxradiusrate) {
 this.mmaxradiusrate = maxradiusrate;
 }

 public void setcolor(int color) {
 mpaint.setcolor(color);
 }

 /**
 * 开始
 */
 public void start() {
 if (!misrunning) {
 misrunning = true;
 mcreatecircle.run();
 }
 }

 /**
 * 停止
 */
 public void stop() {
 misrunning = false;
 }

 protected void ondraw(canvas canvas) {
 iterator<circle> iterator = mcirclelist.iterator();
 while (iterator.hasnext()) {
 circle circle = iterator.next();
 if (system.currenttimemillis() - circle.mcreatetime < mduration) {
 mpaint.setalpha(circle.getalpha());
 canvas.drawcircle(getwidth() / 2, getheight() / 2, circle.getcurrentradius(), mpaint);
 } else {
 iterator.remove();
 }
 }
 if (mcirclelist.size() > 0) {
 postinvalidatedelayed(10);
 }
 }

 public void setinitialradius(float radius) {
 minitialradius = radius;
 }

 public void setduration(long duration) {
 this.mduration = duration;
 }

 public void setmaxradius(float maxradius) {
 this.mmaxradius = maxradius;
 mmaxradiusset = true;
 }

 public void setspeed(int speed) {
 mspeed = speed;
 }

 private void newcircle() {
 long currenttime = system.currenttimemillis();
 if (currenttime - mlastcreatetime < mspeed) {
 return;
 }
 circle circle = new circle();
 mcirclelist.add(circle);
 invalidate();
 mlastcreatetime = currenttime;
 }

 private class circle {
 private long mcreatetime;

 public circle() {
 this.mcreatetime = system.currenttimemillis();
 }

 public int getalpha() {
 float percent = (system.currenttimemillis() - mcreatetime) * 1.0f / mduration;
 return (int) ((1.0f - minterpolator.getinterpolation(percent)) * 255);
 }

 public float getcurrentradius() {
 float percent = (system.currenttimemillis() - mcreatetime) * 1.0f / mduration;
 return minitialradius + minterpolator.getinterpolation(percent) * (mmaxradius - minitialradius);
 }
 }

 public void setinterpolator(interpolator interpolator) {
 minterpolator = interpolator;
 if (minterpolator == null) {
 minterpolator = new linearinterpolator();
 }
 }
}

总结

想必大家看完这篇文章会觉得原来插值器还可以这么用。其实,有些时候我们使用系统提供的api,往往过于局限其中,有时候换个思路,说不定会得到奇妙的效果。以上就是在android实现水波纹特效的全部内容,希望对大家开发android有所帮助。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网