原创作品,转载请注明出处:http://blog.csdn.net/qiujuer/article/details/39998961

做 Android 动画效果一段时间了,感觉深深喜欢上了钻研特效。在手机上显示自己的特效是一件很不错的事情。

废话不多说,前面几天我发布了:[Material Design] 教你做一个Material风格、动画的按钮(MaterialButton)

在其中我讲解了我对 Android L 中 Material 效果的按钮的动画实现方式,今天的文章将基于其上进行进阶讲解新的特效。

在 MaterialButton 中的特效原理是:用户点击时启动一个动画,该动画是在点击位置画颜色渐变同时半径变大的圆,从而实现扩散效果;具体可点击上面的链接查看一下。在按钮中的这样的特效距离谷歌的还是有很大的差距的,下面来对比一下:

官方的:

我们上个版本的:

可以看出谷歌的是有位移效果,而我们的是原地扩散的效果,当然动画速度这个与PS的设置有关,不做比较,实际速度比上面的略快。

下面咱们就来试试做做位移的特效,先画个图给大家看看:

相信大家都能看懂,第一种就是之前的实现方式,只是在原地扩散,第二种就是新的,将在扩散的同时向中心靠拢,且为了达到更加好的视觉效果,靠拢中心的XY轴速度并不是一样的,X轴的靠拢时间=整个扩散时间,向Y轴靠拢的时间~=整个扩散时间*0.3(且都是先快后慢),现在来看看成品效果:

点击中间的时候与第一种差距不大,但是点击两边的时候将会有明显的差距,能感觉到向中心靠拢的触觉。是不是和谷歌的相比起来又近了一些了?

说了这个多的理论与演示,下面来说说整个的实现:

首先我们抽取上一篇文章的成果作为这篇的开头,具体怎么新建控件就不再做介绍了,先看看上一篇的代码成果(该代码进行了一定的修改):

public class MaterialButton extends Button {private static final Interpolator ANIMATION_INTERPOLATOR = new DecelerateInterpolator();private static final long ANIMATION_TIME = 600;private Paint backgroundPaint;private static ArgbEvaluator argbEvaluator = new ArgbEvaluator();private float paintX, paintY, radius;public MaterialButton(Context context) {super(context);init(null, 0);}public MaterialButton(Context context, AttributeSet attrs) {super(context, attrs);init(attrs, 0);}public MaterialButton(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(attrs, defStyle);}@SuppressWarnings("deprecation")private void init(AttributeSet attrs, int defStyle) {...}@SuppressWarnings("NullableProblems")@Overrideprotected void onDraw(Canvas canvas) {canvas.save();canvas.drawCircle(paintX, paintY, radius, backgroundPaint);canvas.restore();super.onDraw(canvas);}@SuppressWarnings("NullableProblems")@Overridepublic boolean onTouchEvent(MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_DOWN) {paintX = event.getX();paintY = event.getY();startRoundAnimator();}return super.onTouchEvent(event);}/*** =============================================================================================* The Animator methods* =============================================================================================*//*** Start Round Animator*/private void startRoundAnimator() {float start, end, height, width;long time = (long) (ANIMATION_TIME * 1.85);//Height Widthheight = getHeight();width = getWidth();//Start Endif (height < width) {start = height;end = width;} else {start = width;end = height;}float startRadius = (start / 2 > paintY ? start - paintY : paintY) * 1.15f;float endRadius = (end / 2 > paintX ? end - paintX : paintX) * 0.85f;//If The approximate square approximate squareif (startRadius > endRadius) {startRadius = endRadius * 0.6f;endRadius = endRadius / 0.8f;time = (long) (time * 0.5);}AnimatorSet set = new AnimatorSet();set.playTogether(ObjectAnimator.ofFloat(this, mRadiusProperty, startRadius, endRadius),ObjectAnimator.ofObject(this, mBackgroundColorProperty, argbEvaluator, attributes.getColor(1), attributes.getColor(2)));// set Timeset.setDuration((long) (time / end * endRadius));set.setInterpolator(ANIMATION_INTERPOLATOR);set.start();}    /*** =============================================================================================* The custom properties* =============================================================================================*/private Property<MaterialButton, Float> mRadiusProperty = new Property<MaterialButton, Float>(Float.class, "radius") {@Overridepublic Float get(MaterialButton object) {return object.radius;}@Overridepublic void set(MaterialButton object, Float value) {object.radius = value;invalidate();}};private Property<MaterialButton, Integer> mBackgroundColorProperty = new Property<MaterialButton, Integer>(Integer.class, "bg_color") {@Overridepublic Integer get(MaterialButton object) {return object.backgroundPaint.getColor();}@Overridepublic void set(MaterialButton object, Integer value) {object.backgroundPaint.setColor(value);}};}

在上述代码中我们实现了点击时进行扩散的效果,初始化控件部分由于我加入了许多的代码这里删除了,具体可以看看我的项目实现,最后会给出地址。

现在基于此开工!

首先我们建立 两个新的属性 分别X坐标与Y坐标属性:

private Property<MaterialButton, Float> mPaintXProperty = new Property<MaterialButton, Float>(Float.class, "paintX") {@Overridepublic Float get(MaterialButton object) {return object.paintX;}@Overridepublic void set(MaterialButton object, Float value) {object.paintX = value;}};private Property<MaterialButton, Float> mPaintYProperty = new Property<MaterialButton, Float>(Float.class, "paintY") {@Overridepublic Float get(MaterialButton object) {return object.paintY;}@Overridepublic void set(MaterialButton object, Float value) {object.paintY = value;}};

在这两个属性中并未调用第一篇所说的 “ invalidate();”方法进行界面刷新,因为该方法应该放在持续时间最长的半径属性中调用。

之后我们获取到高宽 以及根据高和宽 计算出对应的 开始半径与结束半径:

<span style="white-space:pre">  </span>float start, end, height, width, speed = 0.3f;long time = ANIMATION_TIME;//Height Widthheight = getHeight();width = getWidth();//Start Endif (height < width) {start = height;end = width;} else {start = width;end = height;}start = start / 2 > paintY ? start - paintY : paintY;end = end * 0.8f / 2f;//If The approximate square approximate squareif (start > end) {start = end * 0.6f;end = end / 0.8f;time = (long) (time * 0.65);speed = 1f;}

我们首先比较了高与宽的长度 把短的赋予为开始半径 长的赋予为结束半径。

第二步,我们把开始长度除以2  得出其一半的长度 然后与 点击时的Y轴坐标比较,如果Y轴较长则取Y,如果不够则取其相减结果。这样能保证点击开始时的半径能刚好大于其高或者宽(短的一边),这样就不会出现小圆扩散的效果,看起来将会由椭圆的效果(当然以后将会直接画出椭圆)

第三步,我们运算出结束半径,同时保证结束半径为长的一边的一半的8/10 这样的效果是不会出现布满整个控件的情况。8/10 的空间刚好是个不错的选择。

第四步,判断开始长度是否大于结束长度,如果是(近似正方形情况),进行一定规则的重新运算,保证其开始半径能刚好与控件长度差不多(0.48左右),结束半径能刚刚布满控件,同时减少动画时间

当然,我现在才发现了一个BUG,在第二步的地方的BUG,大家看看,希望能提出是哪里的BUG;就当是一个互动!该BUG将会在下个版本修复。

之后我们建立每个属性的动画,并给每个属性动画设置对应的时间:

<span style="white-space:pre">  </span>//PaintXObjectAnimator aPaintX = ObjectAnimator.ofFloat(this, mPaintXProperty, paintX, width / 2);aPaintX.setDuration(time);//PaintYObjectAnimator aPaintY = ObjectAnimator.ofFloat(this, mPaintYProperty, paintY, height / 2);aPaintY.setDuration((long) (time * speed));//RadiusObjectAnimator aRadius = ObjectAnimator.ofFloat(this, mRadiusProperty, start, end);aRadius.setDuration(time);//BackgroundObjectAnimator aBackground = ObjectAnimator.ofObject(this, mBackgroundColorProperty, argbEvaluator, attributes.getColor(1), attributes.getColor(2));aBackground.setDuration(time);

可以看见Y轴的时间乘以了一个speed变量,该变量默认是0.3 如果是近似正方形将初始化为1以便能同时对齐到中心位置,在上一步中有对应变量。

然后咱们把所有的属性动画添加到一个动画集并设置其速度方式为:先快后慢。最后启动该动画集。

        //AnimatorSetAnimatorSet set = new AnimatorSet();set.playTogether(aPaintX, aPaintY, aRadius, aBackground);set.setInterpolator(ANIMATION_INTERPOLATOR);set.start();

以上就是最新的动画效果的实现原理及代码了,当然我们可以将其合并到第一篇的代码中,并使用一个 Bool 属性来控制使用哪一种动画:

public class MaterialButton extends Button {private static final Interpolator ANIMATION_INTERPOLATOR = new DecelerateInterpolator();private static final long ANIMATION_TIME = 600;private Paint backgroundPaint;private static ArgbEvaluator argbEvaluator = new ArgbEvaluator();private float paintX, paintY, radius;private Attributes attributes;public MaterialButton(Context context) {super(context);init(null, 0);}public MaterialButton(Context context, AttributeSet attrs) {super(context, attrs);init(attrs, 0);}public MaterialButton(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(attrs, defStyle);}@SuppressWarnings("deprecation")private void init(AttributeSet attrs, int defStyle) {...}@SuppressWarnings("NullableProblems")@Overrideprotected void onDraw(Canvas canvas) {canvas.save();canvas.drawCircle(paintX, paintY, radius, backgroundPaint);canvas.restore();super.onDraw(canvas);}@SuppressWarnings("NullableProblems")@Overridepublic boolean onTouchEvent(MotionEvent event) {if (attributes.isMaterial() && event.getAction() == MotionEvent.ACTION_DOWN) {paintX = event.getX();paintY = event.getY();if (attributes.isAutoMove())startMoveRoundAnimator();elsestartRoundAnimator();}return super.onTouchEvent(event);}/*** =============================================================================================* The Animator methods* =============================================================================================*//*** Start Round Animator*/private void startRoundAnimator() {float start, end, height, width;long time = (long) (ANIMATION_TIME * 1.85);//Height Widthheight = getHeight();width = getWidth();//Start Endif (height < width) {start = height;end = width;} else {start = width;end = height;}float startRadius = (start / 2 > paintY ? start - paintY : paintY) * 1.15f;float endRadius = (end / 2 > paintX ? end - paintX : paintX) * 0.85f;//If The approximate square approximate squareif (startRadius > endRadius) {startRadius = endRadius * 0.6f;endRadius = endRadius / 0.8f;time = (long) (time * 0.5);}AnimatorSet set = new AnimatorSet();set.playTogether(ObjectAnimator.ofFloat(this, mRadiusProperty, startRadius, endRadius),ObjectAnimator.ofObject(this, mBackgroundColorProperty, argbEvaluator, attributes.getColor(1), attributes.getColor(2)));// set Timeset.setDuration((long) (time / end * endRadius));set.setInterpolator(ANIMATION_INTERPOLATOR);set.start();}/*** Start Move Round Animator*/private void startMoveRoundAnimator() {float start, end, height, width, speed = 0.3f;long time = ANIMATION_TIME;//Height Widthheight = getHeight();width = getWidth();//Start Endif (height < width) {start = height;end = width;} else {start = width;end = height;}start = start / 2 > paintY ? start - paintY : paintY;end = end * 0.8f / 2f;//If The approximate square approximate squareif (start > end) {start = end * 0.6f;end = end / 0.8f;time = (long) (time * 0.65);speed = 1f;}//PaintXObjectAnimator aPaintX = ObjectAnimator.ofFloat(this, mPaintXProperty, paintX, width / 2);aPaintX.setDuration(time);//PaintYObjectAnimator aPaintY = ObjectAnimator.ofFloat(this, mPaintYProperty, paintY, height / 2);aPaintY.setDuration((long) (time * speed));//RadiusObjectAnimator aRadius = ObjectAnimator.ofFloat(this, mRadiusProperty, start, end);aRadius.setDuration(time);//BackgroundObjectAnimator aBackground = ObjectAnimator.ofObject(this, mBackgroundColorProperty, argbEvaluator, attributes.getColor(1), attributes.getColor(2));aBackground.setDuration(time);//AnimatorSetAnimatorSet set = new AnimatorSet();set.playTogether(aPaintX, aPaintY, aRadius, aBackground);set.setInterpolator(ANIMATION_INTERPOLATOR);set.start();}/*** =============================================================================================* The custom properties* =============================================================================================*/private Property<MaterialButton, Float> mPaintXProperty = new Property<MaterialButton, Float>(Float.class, "paintX") {@Overridepublic Float get(MaterialButton object) {return object.paintX;}@Overridepublic void set(MaterialButton object, Float value) {object.paintX = value;}};private Property<MaterialButton, Float> mPaintYProperty = new Property<MaterialButton, Float>(Float.class, "paintY") {@Overridepublic Float get(MaterialButton object) {return object.paintY;}@Overridepublic void set(MaterialButton object, Float value) {object.paintY = value;}};private Property<MaterialButton, Float> mRadiusProperty = new Property<MaterialButton, Float>(Float.class, "radius") {@Overridepublic Float get(MaterialButton object) {return object.radius;}@Overridepublic void set(MaterialButton object, Float value) {object.radius = value;invalidate();}};private Property<MaterialButton, Integer> mBackgroundColorProperty = new Property<MaterialButton, Integer>(Integer.class, "bg_color") {@Overridepublic Integer get(MaterialButton object) {return object.backgroundPaint.getColor();}@Overridepublic void set(MaterialButton object, Integer value) {object.backgroundPaint.setColor(value);}};}

在最后附上两种方式运行后的效果对比图:

还不错吧?要是感觉比较和你的胃口,这里有我的整个项目:

Genius-Android

APK与动画MP4

最新文章终极解决方案(2015-01-07):

打造极致Material Design动画风格Button

[Material Design] MaterialButton 效果进阶 动画自动移动进行对齐效果相关推荐

  1. ANDROID L——Material Design详解(动画篇)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! Android L: Google已经确认Android L就是Android Lolli ...

  2. android+水滴粘性动画,纯css实现Material Design中的水滴动画按钮

    前言 大家平时应该经常见到这种特效,很炫酷不是吗 这是谷歌Material Design中最常见的特效了,市面上也有很多现成的js库,用来模拟这一特效.但是往往要引入一大堆js和css,其实在已有的项 ...

  3. 怎么用java做水滴动画_‘纯css实现Material Design中的水滴动画按钮’的js体验优化...

    前言 在上一篇,我们已经实现了用纯css实现水滴扩散动画,但是有一些瑕疵,文章结尾处也提到过,一是页面加载进来就会看到按钮上的水滴动画运动一次,二是点击的时候不能根据鼠标的位置来扩散,今天我们来解决这 ...

  4. [Material Design] 教你做一个Material风格、动画的button(MaterialButton)

    原创作品,转载请注明出处:http://blog.csdn.net/qiujuer/article/details/39831451 前段时间Android L 公布了,相信看过公布会了解过的朋友都为 ...

  5. ANDROID L——Material Design综合应用(Demo)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! Material Design: Material Design是Google推出的一个全 ...

  6. html卡片布局按钮,Material Design风格动态网格卡片布局UI设计

    这是一款效果非常炫酷的Material Design风格动态网格卡片布局UI设计效果.在该效果中鼠标滑过卡片时会有阴影效果,当浏览器尺寸改变时,卡片大小会随着改变,整个网格布局有动态变化效果. 使用方 ...

  7. App 界的一股清流 音视频应有尽有 完全按照 Material design 规范设计的 App

    vld 项目地址:Cuieney/vld  简介:App 界的一股清流 音视频应有尽有 完全按照 Material design 规范设计的 App 更多:作者   提 Bug    标签: andr ...

  8. 一款在线视频 App,基于 Material Design + MVP + RxJava + Retrofit + Realm + Glide

    Ghost 项目地址:GeekGhost/Ghost 简介:一款在线视频 App,基于 Material Design + MVP + RxJava + Retrofit + Realm + Glid ...

  9. ANDROID L——Material Design详解(视图和阴影)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! Android L: Google已经确认Android L就是Android Lolli ...

  10. Android(Lollipop/5.0) Material Design(六) 使用图像

    Material Design列 Android(Lollipop/5.0) Material Design(一) 简单介绍 Android(Lollipop/5.0) Material Design ...

最新文章

  1. [Hibernate] - Annotations - One To One
  2. mysql怎么升级_MySQL UPDATE 更新
  3. JSP中 request.getRealPath(/xx/yy) 方法提示已经过时的替代方法
  4. 深度学习工具caffe详细安装指南
  5. 正则表达式强化,爬虫练习
  6. 使用MariaDB后端在Ubuntu 18.04 / Debian 9上安装PowerDNS和PowerDNS-Admin
  7. 【HDU1166】敌兵布阵,线段树练习
  8. 抹去阴影,搞定了Cyclone III
  9. d3js scales深入理解
  10. JavaScript的预编译过程分析
  11. 推荐几个好用的插件(Edge)
  12. 28个超有用的PPT小技巧,快来收藏
  13. 应用密码学笔记第五章-第六章
  14. 学习笔记(14):程序员的数学:微积分-常用导数(一):最常用到的技巧
  15. OEM、ODM模式区别
  16. Java SE第10章 异常处理
  17. webscraper改写案例分析
  18. ORAN专题系列-11:5G O-RAN RIC的PaaS和SaaS是封闭与开放的融合
  19. 如何画一条0.5px的边(细线)
  20. html字母上方加箭头,html5怎么在图片上加左右箭头

热门文章

  1. n倍角公式的行列式形式与证明
  2. vue 背景弹出禁止滚动_vue-蒙层弹窗里的内容滚动。外层大页面禁止滚动
  3. Raki的读paper小记:Star-Transformer
  4. 重装系统后如何配置网络
  5. 自适应的设置字体的方式
  6. 使用flask开发web应用
  7. 从JS库的使用者转变成JS的开发者——第一篇 javascript对象
  8. [原创]Jenkins持续集成工具介绍
  9. Oracle基本数据范例存储名堂浅析
  10. 一段挺有用的小SCRIPT