Android特效之水波纹的实现

前言

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

支付宝 "咻咻咻" 式

流量球 "荡漾" 式

真实的水波纹效果,基于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有所帮助。

(0)

相关推荐

  • Android实现水波纹效果

    一.效果 点击开始: 点击停止: 二.在MainActivity中 import android.graphics.Paint; import android.os.Bundle; import android.support.v4.view.animation.LinearOutSlowInInterpolator; import android.support.v7.app.AppCompatActivity; import android.view.View; import android

  • Android仿水波纹流量球进度条控制器

    仿水波纹流球进度条控制器,Android实现高端大气的主流特效,供大家参考,具体内容如下 效果图: CircleView 这里主要是实现中心圆以及水波特效 package com.lgl.circleview; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.gra

  • Android自定义View 实现水波纹动画引导效果

    一.实现效果图 二.实现代码 1.自定义view package com.czhappy.showintroduce.view; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Pat

  • Android项目实战手把手教你画圆形水波纹loadingview

    本文实例讲解的是如何画一个满满圆形水波纹loadingview,这类效果应用场景很多,比如内存占用百分比之类的,分享给大家供大家参考,具体内容如下 效果图如下: 预备的知识: 1.贝塞尔曲线    如果你不了解,可以来这里进行基础知识储备:神奇的贝塞尔曲线 2.Paint.setXfermode()  以及PorterDuffXfermode 千万不要被这个b的名字吓到,不熟悉看到可能会认为很难记,其实 只要站在巨人的丁丁上 还是很简单的. 好了 废话不多说 ,跟我一步步来做一个炫酷的view吧

  • Android自定义WaveProgressView实现水波纹加载需求

    先看效果图: 你可以定义成你项目的logo图片,可以设置水波颜色.波长.波宽.字体大小.颜色.进度条的最大值,当前进度值,还可以设置波纹震动的快慢.当设置一个进度不变的时候,打开时还有一个动画填满的效果(比如第二个流量显示,这里图片没有截出这个效果). 源码地址 1. 如何使用 1.1 在布局文件中 添加自定义控件: <cn.fanrunqi.waveprogressview.WaveProgressView android:id="@+id/waveProgressbar" a

  • Android 自定义view实现水波纹动画效果

    在实际的开发中,很多时候还会遇到相对比较复杂的需求,比如产品妹纸或UI妹纸在哪看了个让人兴奋的效果,兴致高昂的来找你,看了之后目的很明确,当然就是希望你能给她: 在这样的关键时候,身子板就一定得硬了,可千万别说不行,爷们儿怎么能说不行呢: 好了,为了让大家都能给妹纸们想要的,后面会逐渐分享一些比较比较不错的效果,目的只有一个,通过自定义view实现我们所能实现的动效: 今天主要分享水波纹效果: 1.标准正余弦水波纹: 2.非标准圆形液柱水波纹: 虽说都是水波纹,但两者在实现上差异是比较大的,一个

  • Android实现自定义华丽的水波纹效果

    先来看看效果 实现效果 模拟水波纹的效果:点击屏幕就有圆环出现,半径从小到大,透明度从大到小(0为透明) 实现思路 1.自定义类继承View. 2.定义每个圆环的实体类 Wave,并初始化绘制圆环的画笔的数据. 3.重写onTouchEvent方法,down时,获得坐标点,做为圆环圆心. 4.发送handler信息,对数据进行修改,刷新页面. 5.重写onDraw方法,绘制一个圆环. 1. 自定义类继承View 新建WaterWaveView2类继承View public class Water

  • Android自定义view实现水波纹进度球效果

    今天我们要实现的这个view没有太多交互性的view,所以就继承view. 自定义view的套路,套路很深 1.获取我们自定义属性attrs(可省略) 2.重写onMeasure方法,计算控件的宽和高 3.重写onDraw方法,绘制我们的控件 这么看来,自定义view的套路很清晰嘛. 我们看下今天的效果图,其中一个是放慢的效果(时间调的长) 我们按照套路来. 一.自定义属性 <declare-styleable name="WaveProgressView"> <at

  • Android实现兼容的水波纹效果

    先看看效果图 其实,要实现这一效果很简单,只要分drawable和drawablev21两个文件夹就好了. 普通情况下的selector: <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@c

  • Android实现点击Button产生水波纹效果

    先上图,看看接下来我要向大家介绍的是个什么东西,如下图: 接下来要介绍的就是如何实现上述图中的波纹效果,这种效果如果大家没有体验过的话,可以看看百度手机卫士或者360手机卫士,里面的按钮点击效果都是这样的,另外Android 5.0以上的版本也出现了这种效果.不多说,下面聊聊具体的怎么实现. 首先大家看到的是三个button,水波纹的出现给我们的错觉是直接将波纹绘制在button上面的,但是这样能做到吗?首先button自己有background和src,如果把半透明的水波纹当作backgrou

随机推荐