前言
水波纹特效,想必大家或多或少见过,在我的印象中,大致有如下几种:
支付宝 "咻咻咻" 式
流量球 "荡漾" 式
真实的水波纹效果,基于Bitmap处理式
话不多说,先来看看效果:
填充式水波纹,间距相等
@H_502_25@
非填充式水波纹,间距相等
非填充式水波纹,间距不断变大
填充式水波纹,间距不断变小
想必大家已经知道基本的原理了,就是用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,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有所帮助。
。
以上是内存溢出为你收集整理的Android特效之水波纹的实现全部内容,希望文章能够帮你解决Android特效之水波纹的实现所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)