文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android特效之水波纹的实现

2022-06-06 07:54

关注

前言

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

     支付宝 "咻咻咻" 式

     流量球 "荡漾" 式

     真实的水波纹效果,基于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的所有代码:



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有所帮助。

您可能感兴趣的文章:Android实现点击Button产生水波纹效果Android项目实战手把手教你画圆形水波纹loadingviewAndroid仿水波纹流量球进度条控制器Android自定义View 实现水波纹动画引导效果Android实现水波纹效果Android 自定义view实现水波纹动画效果Android实现自定义华丽的水波纹效果Android自定义view实现水波纹进度球效果Android实现兼容的水波纹效果flutter InkWell实现水波纹点击效果


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯