欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android特效之水波纹的实现

程序员文章站 2024-03-07 10:55:14
前言 水波纹特效,想必大家或多或少见过,在我的印象中,大致有如下几种:      支付宝 "咻咻咻" 式  &n...

前言

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

     支付宝 "咻咻咻" 式

     流量球 "荡漾" 式

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

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

填充式水波纹,间距相等

Android特效之水波纹的实现

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

Android特效之水波纹的实现

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

Android特效之水波纹的实现

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

Android特效之水波纹的实现

想必大家已经知道基本的原理了,就是用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有所帮助。