先上效果图

动画可以分割为3阶段,第一阶段是6个小圆的旋转,第二阶段是6个小圆的扩散和收缩,第三部分是水波纹特效,动画的实现也是按照这三个阶段进行实现的。

1.初始化

自定义FlashView继承View,然后在构造方法中对画笔初始化,初始化2支画笔,一个是画小圆的,另一个是第三阶段画水波纹的

    private void init() {setBackgroundResource(R.drawable.girl);//先设置一张背景,模拟splash后面的界面mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mHolePaint = new Paint(Paint.ANTI_ALIAS_FLAG);mHolePaint.setStyle(Paint.Style.STROKE);mHolePaint.setColor(Color.WHITE);//扩散圆的颜色为白色mSplashState = new RotateState();}

有一堆成员变量的声明,每个都有详细注释,一看就懂

   private Paint mPaint;//画小圆的画笔private Paint mHolePaint;//画水波纹的画笔private int[] circleColors = {0xFFFF9600, 0xFF02D1AC, 0xFFFFD200, 0xFF00C6FF, 0xFF00E099, 0xFFFF3892};//6个小圆的颜色的数组private float mCenterX;//旋转圆的中心横坐标private float mCenterY;//旋转圆的中心纵坐标private float mDistance;//表示斜对角线长度的一半,扩散圆最大半径private float mCircleRadius = 18; //6个小球的半径private float mRotateRadius = 90;//旋转大圆的半径private float mCurrentRotateRadius = mRotateRadius;//当前旋转圆的半径private float mCurrentRotateAngle = 0F; //当前旋转圆的旋转角度private float mCurrentHoleRadius = 0F;//扩散圆的半径private int mRotateDuration = 1000; //旋转动画的时长private ValueAnimator mValueAnimator;//属性动画private SplashState mSplashState; 

旋转圆的中心坐标和水波纹扩散圆的最大半径需要根据屏幕尺寸初始化,因此放到onMeasure中

    @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);mCenterX = MeasureSpec.getSize(widthMeasureSpec) / 2;mCenterY = MeasureSpec.getSize(heightMeasureSpec) / 2;mDistance = (float) Math.hypot(mCenterX, mCenterY); //hypot函数是取平方根}

2.绘制小圆

到这里初始化工作已经做完了。在三个动画阶段开始之前先来绘制6个小球,动画的事往后稍一稍。首先定义一个抽象类SplashState,包含一个抽象方法drawState。后面的三种动画都继承自SplashState实现drawState方法,然后我们只需要在ondraw方法中调用SplashState的drawStatue方法即可,具体的实现在每个状态的drawState方法中

    private abstract class SplashState {abstract void drawState(Canvas canvas);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);mSplashState.drawState(canvas);}

绘制小圆在第一个阶段旋转动画中实现,么得办法,总得找个地方去绘制,第一个是绘制小圆的背景,纯白色,第二个是绘制6个小圆,绘制背景很简单,直接绘制纯色就好,小圆的绘制稍微有一点点绕,小圆的颜色直接从定义的颜色数组里面拿,每个小圆的横坐标等于旋转的大圆半径加上旋转的大圆半径在每个小圆在极坐标中的角度的cos值,以第二个小球为例,画图可能更清晰点

    private class RotateState extends SplashState {@Overridevoid drawState(Canvas canvas) {drawBackground(canvas);//画背景drawCircles(canvas);//画小圆}}/*** 画背景*/private void drawBackground(Canvas canvas) {canvas.drawColor(Color.WHITE);//绘制白色背景}/*** 画小圆*/private void drawCircles(Canvas canvas) {for (int i = 0; i < circleColors.length; i++) {float rotate = (float) (i * Math.PI * 2 / circleColors.length) + mCurrentRotateAngle;mPaint.setColor(circleColors[i]);float cX = (float) (mCenterX + (mCurrentRotateRadius * Math.cos(rotate)));float cY = (float) (mCenterY + (mCurrentRotateRadius * Math.sin(rotate)));canvas.drawCircle(cX, cY, mCircleRadius, mPaint);}}

到这里6个小圆已经绘制出来了。

3.旋转动画

接下来就是让小球球动起来,动起来就需要用到动画了,在这里使用属性动画,属性动画定义在RotateState的构造方法中,因此我们在一开始的init方法中创建对象的时候便开始的动画。属性动画数值从0变化到2*π,就是整整一圈,然后再属性动画的值变化的时候改变当前旋转角度,然后调用invalidate方法,invalidate方法会触发ondraw方法,ondraw方法中又会调用到RotateState的drawstate方法,从而实现了旋转动画,当然还要监听旋转动画执行完毕,执行完毕后我们去启动第二阶段的动画代码比较简单,直接上

    private class RotateState extends SplashState {private RotateState() {mValueAnimator = ValueAnimator.ofFloat(0, (float) Math.PI * 2);mValueAnimator.setInterpolator(new LinearInterpolator());mValueAnimator.setDuration(mRotateDuration);mValueAnimator.setRepeatCount(1);mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mCurrentRotateAngle = (float) animation.getAnimatedValue();invalidate();}});mValueAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);mSplashState = new MerginState();}});mValueAnimator.start();}@Overridevoid drawState(Canvas canvas) {drawBackground(canvas);//画背景drawCircles(canvas);//画小圆}}

4.扩散聚合动画

我们监听了第一阶段动画的结束,结束后new了一个MerginState,并赋值给mSplashState,从现在开始ondraw调用的drawState将变为MerginState的,扩散聚合动画我们是反正来的,动画的start是正向播放动画,reverse则是反正播放动画,所以我们要实现的是把旋转圆的半径从0扩散到大于旋转时候的半径值,然后再缩小到旋转时的半径值,这就要使用到OvershootInterpolator差值器了,这个差值器是开始的时候向后然后向前甩一定值后返回最后的值,关于差值器,可以参考https://blog.csdn.net/harvic880925/article/details/40049763#commentBox,按照反着来外加这个差值器,便实现了我们的效果,当然还是要监听下动画播放完毕进入第三阶段的水波纹,整个扩散聚合代码如下

    private class MerginState extends SplashState {private MerginState() {mValueAnimator = ValueAnimator.ofFloat(0, mRotateRadius);mValueAnimator.setDuration(mRotateDuration);mValueAnimator.setRepeatCount(0);mValueAnimator.setInterpolator(new OvershootInterpolator());mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mCurrentRotateRadius = (float) animation.getAnimatedValue();invalidate();}});mValueAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {mSplashState = new ExpandState();}});mValueAnimator.reverse();}@Overridevoid drawState(Canvas canvas) {drawBackground(canvas);//画背景drawCircles(canvas);//画小圆}}

5.水波纹动画

水波纹动画的实现思路也不难,就是画一个圆,画之前将画笔设置为空心圆(mHolePaint.setStyle(Paint.Style.STROKE)),然后我们不停地改变圆环的粗细,实现水波纹的效果,空心圆的半径等于mDistance,即这个圆正好可以把整个屏幕扩住,然后一开始圆环的宽度等于圆的半径的2倍,这样整个空心圆已经把屏幕填满,然后动态的减小圆环的宽度到0,这样看起来的效果刚好就是我们要的,在这之前我们应该改变一个drawBackground方法,前两个阶段我们绘制的是整个屏幕白色,现在只是绘制圆环为白色

    private void drawBackground(Canvas canvas) {if (mCurrentHoleRadius > 0) {mHolePaint.setStrokeWidth(2 * (mDistance - mCurrentHoleRadius));canvas.drawCircle(mCenterX, mCenterY, mDistance, mHolePaint);} else {canvas.drawColor(Color.WHITE);//绘制白色背景}}private class ExpandState extends SplashState {private ExpandState() {mValueAnimator = ValueAnimator.ofFloat(0, mDistance);mValueAnimator.setDuration(mRotateDuration);mValueAnimator.setInterpolator(new LinearInterpolator());mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mCurrentHoleRadius = (float) animation.getAnimatedValue();invalidate();}});mValueAnimator.start();}@Overridevoid drawState(Canvas canvas) {drawBackground(canvas);//画背景}}

整个动画的过程就算结束了,完整的代码

public class SplashView extends View {private Paint mPaint;//画小圆的画笔private Paint mHolePaint;//画水波纹的画笔private int[] circleColors = {0xFFFF9600, 0xFF02D1AC, 0xFFFFD200, 0xFF00C6FF, 0xFF00E099, 0xFFFF3892};//6个小圆的颜色的数组private float mCenterX;//旋转圆的中心横坐标private float mCenterY;//旋转圆的中心纵坐标private float mDistance;//表示斜对角线长度的一半,扩散圆最大半径private float mCircleRadius = 18; //6个小球的半径private float mRotateRadius = 90;//旋转大圆的半径private float mCurrentRotateRadius = mRotateRadius;//当前旋转圆的半径private float mCurrentRotateAngle = 0F; //当前旋转圆的旋转角度private float mCurrentHoleRadius = 0F;//扩散圆的半径private int mRotateDuration = 1000; //旋转动画的时长private ValueAnimator mValueAnimator;//属性动画private SplashState mSplashState;public SplashView(Context context) {this(context, null);}public SplashView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public SplashView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {setBackgroundResource(R.drawable.girl);//先设置一张背景,模拟splash后面的界面mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mHolePaint = new Paint(Paint.ANTI_ALIAS_FLAG);mHolePaint.setStyle(Paint.Style.STROKE);mHolePaint.setColor(Color.WHITE);//扩散圆的颜色为白色mSplashState = new RotateState();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);mCenterX = MeasureSpec.getSize(widthMeasureSpec) / 2;mCenterY = MeasureSpec.getSize(heightMeasureSpec) / 2;mDistance = (float) Math.hypot(mCenterX, mCenterY); //hypot函数是取平方根}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);mSplashState.drawState(canvas);}/*** 抽象类,三种状态继承自该类,实现drawState*/private abstract class SplashState {abstract void drawState(Canvas canvas);}/*** 1.旋转动画*/private class RotateState extends SplashState {private RotateState() {mValueAnimator = ValueAnimator.ofFloat(0, (float) Math.PI * 2);mValueAnimator.setInterpolator(new LinearInterpolator());mValueAnimator.setDuration(mRotateDuration);mValueAnimator.setRepeatCount(1);mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mCurrentRotateAngle = (float) animation.getAnimatedValue();invalidate();}});mValueAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);mSplashState = new MerginState();}});mValueAnimator.start();}@Overridevoid drawState(Canvas canvas) {drawBackground(canvas);//画背景drawCircles(canvas);//画小圆}}/*** 2.扩散后缩放动画*/private class MerginState extends SplashState {private MerginState() {mValueAnimator = ValueAnimator.ofFloat(0, mRotateRadius);mValueAnimator.setDuration(mRotateDuration);mValueAnimator.setRepeatCount(0);mValueAnimator.setInterpolator(new OvershootInterpolator());mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mCurrentRotateRadius = (float) animation.getAnimatedValue();invalidate();}});mValueAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {mSplashState = new ExpandState();}});mValueAnimator.reverse();}@Overridevoid drawState(Canvas canvas) {drawBackground(canvas);//画背景drawCircles(canvas);//画小圆}}/*** 3.水波纹动画*/private class ExpandState extends SplashState {private ExpandState() {mValueAnimator = ValueAnimator.ofFloat(0, mDistance);mValueAnimator.setDuration(mRotateDuration);mValueAnimator.setInterpolator(new LinearInterpolator());mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mCurrentHoleRadius = (float) animation.getAnimatedValue();invalidate();}});mValueAnimator.start();}@Overridevoid drawState(Canvas canvas) {drawBackground(canvas);//画背景}}/*** 画小圆*/private void drawCircles(Canvas canvas) {for (int i = 0; i < circleColors.length; i++) {float rotate = (float) (i * Math.PI * 2 / circleColors.length) + mCurrentRotateAngle;mPaint.setColor(circleColors[i]);float cX = (float) (mCenterX + (mCurrentRotateRadius * Math.cos(rotate)));float cY = (float) (mCenterY + (mCurrentRotateRadius * Math.sin(rotate)));canvas.drawCircle(cX, cY, mCircleRadius, mPaint);}}/*** 画背景*/private void drawBackground(Canvas canvas) {if (mCurrentHoleRadius > 0) {mHolePaint.setStrokeWidth(2 * (mDistance - mCurrentHoleRadius));canvas.drawCircle(mCenterX, mCenterY, mDistance, mHolePaint);} else {canvas.drawColor(Color.WHITE);//绘制白色背景}}}

附上Demo:https://github.com/987570437/PaintDemo

Android Canvas进阶之自定义View实现Splash的旋转、扩散聚合、水波纹特效相关推荐

  1. HenCoder Android 开发进阶:自定义 View 1-5 绘制顺序

    这期是 HenCoder 自定义绘制的第 1-5 期:绘制顺序 之前的内容在这里:  HenCoder Android 开发进阶 自定义 View 1-1 绘制基础  HenCoder Android ...

  2. Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)

    转载请注明地址:http://blog.csdn.net/xiaanming/article/details/10298163 很多的时候,系统自带的View满足不了我们功能的需求,那么我们就需要自己 ...

  3. android canvas 手写,自定义view—Canvas实现手写板和涂鸦功能

    学习导航 第一节:http://blog..net/bobo8945510/article/details/53197727 -自定义View-自定义属性及引用 第二节:http://blog..ne ...

  4. android自定义控件几种,Android 自定义View一个控件搞定多种水波纹涟漪扩散效果 - CSDN博客...

    效果图 实现思路 这个效果实现起来并不难,重要的是思路 此View满足了多种水波纹涟漪扩散效果,这要求它能满足很多的变化 根据上面的样式,可以看出此View需要满足以下变化 圆圈从中心可循环向外扩散 ...

  5. Android进阶之自定义View实战(二)九宫格手势解锁实现

    一.引言 在上篇博客Android进阶之自定义View实战(一)仿iOS UISwitch控件实现中我们主要介绍了自定义View的最基本的实现方法.作为自定义View的入门篇,仅仅介绍了Canvas的 ...

  6. Android 气泡动画(自定义View类)

    Android 气泡动画(自定义View类) 一.前言 二.代码 1. 随机移动的气泡 2.热水气泡 一.前言 最近有需求制作一个水壶的气泡动画,首先在网上查找了一番,找到了一个文章. https:/ ...

  7. 超酷的计步器APP(一)——炫酷功能实现,自定义水波纹特效、自定义炫酷开始按钮、属性动画的综合体验

    超酷的计步器APP(一)--炫酷功能实现,自定义水波纹特效.自定义炫酷开始按钮.属性动画的综合体验 好久没写博客了,没给大家分享技术了,真是有些惭愧.这段时间我在找工作,今年Android的行情也不怎 ...

  8. Android水波纹特效的简单实现

    我的开源页面指示器框架 MagicIndicator,各位一定不要错过哦. 水波纹特效,想必大家或多或少见过,在我的印象中,大致有如下几种: 支付宝 "咻咻咻" 式 流量球 &qu ...

  9. android 换行模式,Android进阶之自定义View(1)实现可换行的TextView

    今天来一起学习一下最简单的自定义view,自己动手写一个MyTextView,当然不会像系统的TextView那么复杂,只是实现一下TextView的简单功能,包括分行显示及自定义属性的处理,主要目的 ...

最新文章

  1. Android程序的反编译对抗研究
  2. Javascript的prototype
  3. android多线程下载原理,安卓多线程断点续传下载功能(靠谱第三方组件,原理demo)...
  4. 让Elasticsearch飞起来!——性能优化实践干货
  5. 微信小程序开发-入门基础
  6. MyBatisPlus条件构造器排序方法orderByDesc参数怎样构造
  7. hdu2019——数列有序解题报告
  8. C语言随笔小算法:单向链表
  9. 尺取法---poj3601
  10. scratch python插件_Scratch3.0设计的插件系统(上篇)
  11. STC8H开发(十四): I2C驱动RX8025T高精度实时时钟芯片
  12. js 解析lrc文件(歌词)
  13. Hexo | yilia主题美化
  14. 怎样防止表单重复提交
  15. c语言输入密码并将密码掩盖住
  16. 用电脑玩创造与魔法还要申请模拟器白名单?不存在的
  17. [分形学] Julia Set (茱莉亚集) VC 源代码
  18. 微信小程序如何添加业务域名
  19. 将 MobaXterm 的高亮/突出显示集功能(HighLight sets)移植到 Xshell 中使用
  20. 赞,全网开发者都在学的26个课程!

热门文章

  1. 嵌入式平台的VGA接口设计及VGA接口时序波形图
  2. CF1143D/1142A The Beatles
  3. 超级淘的安全性,科普一下超级淘到底有多安全
  4. 信息系统项目管理师学习笔记11-项目风险管理
  5. 虎扑体育网csrf漏洞
  6. 2017java开发新技术
  7. Ubuntu20.04 卸载cuda 11.0
  8. 使用Holer公网SSH访问内网(局域网)Linux系统
  9. 郑豪8.31月K收官提防大洗盘,日K箱体先高空,黄金亚欧盘最新操作建议
  10. Python爬虫学习记录(3)——用Python获取虾米加心歌曲,并获取MP3下载地址