前言:这段时间一直在对Android的动画效果做一些研究,整理了一些效果。我很喜欢一句话: 做事情一定要有产出
,所以又到了开始总结的时刻了。好了,废话不多说,开始总结。
安卓提供的动画主要分为两种:属性动画(Property Animation)
和传统动画 (View Animation)
。而View Animation又分为补间动画(TweenAnimation)
和帧动画(FrameAnimation)
。
传统动画
补间动画
补间动画可以分为四种形式,分别是 alpha(淡入淡出)
,rotate(旋转)
,scale(缩放大小)
,translate(位移)
。一般会采用xml 文件的形式进行编写,代码会更容易书写和阅读,同时也更容易复用。Tween动画使用起来简单,效果也比较流畅
,但是缺点也是很明显,首先就是拓展性太差
,只能写移动、缩放、旋转、渐变四种动画,以及这四种动画的组合,不支持自定义View的拓展。其次一个致命的缺点就是动画不具有交互性
,当某个元素发生View 动画后,其响应事件的位置仍然在动画进行前的地方。所以View 动画只能作为普通的动画效果,要避免涉及交互操作。
首先,在res/anim/ 文件夹下定义如下的动画实现 anim_alpha.xml :
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="1000"
android:fromAlpha="1"
android:repeatCount="1"
android:repeatMode="reverse"
android:toAlpha="0.3" />
</set>
在java代码中调用:
Animation animation = AnimationUtils.loadAnimation(mContext, R.anim.alpha_anim);
img = (ImageView) findViewById(R.id.img);
img.startAnimation(animation);
OK,下面看看我整理的四个动画效果:
帧动画
帧动画
是最容易实现的一种动画,这种动画更多的依赖于完善的UI资源,他的原理就是将一张张单独的图片连贯的进行播放。当然他的缺点也很明显,占用资源过多。
首先,在res/drawable/ 文件夹下定义如下的动画实现 animation_list.xml :
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@mipmap/nine"
android:duration="250" />
<item
android:drawable="@mipmap/eight"
android:duration="250" />
<item
android:drawable="@mipmap/seven"
android:duration="250" />
<item
android:drawable="@mipmap/six"
android:duration="250" />
<item
android:drawable="@mipmap/five"
android:duration="250" />
<item
android:drawable="@mipmap/six"
android:duration="250" />
<item
android:drawable="@mipmap/four"
android:duration="250" />
<item
android:drawable="@mipmap/three"
android:duration="250" />
<item
android:drawable="@mipmap/two"
android:duration="250" />
<item
android:drawable="@mipmap/one"
android:duration="250" />
<item
android:drawable="@mipmap/zero"
android:duration="250" />
</animation-list>
在java代码中调用:
iv_frame.setImageResource(R.drawable.animation_list);
AnimationDrawable anim = (AnimationDrawable) iv_frame.getDrawable();
// 是否只播放一次
anim.setOneShot(true);
anim.start();
还是继续展示效果:
属性动画
在Android 3.0以后,谷歌推出了新的动画框架——属性动画
,帮助开发者实现更加丰富的动画效果。它不仅能实现所有Tween动画的功能,还有很强的拓展性,根本原因是属性动画从本质上已经完全摆脱了控件,虽然我们大多数情况下使用属性动画都是给控件做动画,但是属性动画的底层只是一个数值发生器,和控件并没有关系。
数值发生器:ValueAnimator
数值发生器,这是属性动画的根本,ValueAnimator
的功能就是在两个数值范围内,顺序地产生过渡数值,过渡速率可以通过Intepolator
来控制,过渡时间通过duration
来设置,最终产生一组有特定变化速率的数值序列,然后拿这些数值去改变控件的状态,进而实现了动画的效果。
属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等。
java代码实现:
// 创建 ValueAnimator 类 参数: 设置属性数值的初始值 & 结束值
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
//设置动画运行时长
valueAnimator.setDuration(1000);
// 设置更新监听器:即数值每次变化更新都会调用该方法
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//获得每次变化后的属性值
float f = valueAnimator.getAnimatedFraction();
//每次值变化时,将值手动赋值给对象的属性
iv_value_animator.setRotationX(f * 359);
}
});
//开始动画
valueAnimator.start();
下面看下效果:
继续展示valueAnimator 实现的几个动画效果,代码请参考最下方的github。
ObjectAnimator
ObjectAnimator
是属性动画中比较重要的一个类,能够直接对对象的属性值进行改变操作,从而实现动画效果。ObjectAnimator 参数包括一个对象和对象的属性名字,但这个属性必须有set
和 get
方法,其内部会通过Java 反射机制
来调用set 方法来修改对象的属性值。
1.如直接改变 View的 rotation 属性 从而实现旋转的动画效果
2.继承自ValueAnimator类,即底层的动画实现机制是基于ValueAnimator类
比如我们要实现ValueAnimator上面的效果,Java 代码如下:
//创建 ObjectAnimator 类 参数: 1.要操作的控件 2.要操作的属性 3.可变的float 类型数组
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(iv_object_animator, "rotationX", 0, 359);
//设置动画运行时长
objectAnimator.setDuration(1000);
//开始动画
objectAnimator.start();
Path 动画
ObjectAnimator提供了一种根据path 轨迹
的方式来实现动画的效果,即控件根据给定的path 路径实现进行移动,进而实现相应的动画。
我们要实现一些比较复杂的路径,必须要了解一个很重要的东西——贝塞尔曲线
。可参考链接:贝塞尔曲线的详细介绍介绍与公式,了解贝塞尔曲线后,我们就可以用代码画出我们想要的路径了。当我们只获取到一个曲线,无法知道这个曲线相应的控制点时,可以利用PS工具来估算出大概的控制点,操作过程如下:
1、先选择钢笔工具,选择起始点,沿着曲线的路径拉一条直线
2、再选择终止点,沿着曲线的路径拉一条直线 ,这样就拉出一个曲线
3、然后用直接选择工具微调曲线,这样三阶贝塞尔曲线的控制点就出现了,然后根据像素估算
以上方法是我根据网上的资料自己总结的,如果有更好的办法可以相互交流。
先展示一个根据path 实现的动画,上效果图:
而代码的实现也很简单:
ObjectAnimator animator = ObjectAnimator.ofFloat(iv_circle, View.X, View.Y, curvepath.path);
animator.setDuration(2000);
animator.start();
为了方便我们对ValueAnimator
+ObjectAnimator
+Path
动画有个深刻的了解,我整理了一个动画的实战演练。这个实战是参照十分钟搞定酷炫动画,Android自定义 View 入门 这位博主的写的,自己根据他的源码分析了下,感觉很不错。OK,先上效果图。
代码请参考最下方的github。
CircularReveral
这是Android5.0推出的新的动画框架,可以给View做一个揭露
效果
当您显示或隐藏一组 UI 元素时,揭露动画可为用户提供视觉连续性。ViewAnimationUtils.createCircularReveal(View, int, int, float, float))方法让您能够为裁剪区域添加动画以揭露或隐藏视图。五个参数分别是View,中心点坐标,开始半径,结束半径。通过这五个参数的配合,我们可以做出很多不同的效果。
先看下效果:
实现代码如下:
//参数 View,中心点坐标,开始半径,结束半径
Animator animator = ViewAnimationUtils.createCircularReveal(ivOval, ivOval.getWidth() / 2,ivOval.getHeight() / 2, 0, ivOval.getWidth());
animator.setDuration(2000);
animator.start();
转场动画
Android 5.0新增了4种转场动画,分别是Explode
、Slide
、Fade
、Share
。传统的转场动画只能作用于整个页面,不能对页面中的单个元素做控制,而5.0新转场动画可以控制页面中的每个元素。
Explode: 下一个页面的元素从四面八方进入,最终形成完整的页面
Slide: 下一个页面元素从底部依次向上运动,最终形成完整的页面
Fade: 下一个页面元素渐变出现,最终形成完整的页面
Share: 在跳转的两个Activity之间,如果有相同的View元素,那么,两个元素就可以设置成共享状态,在跳转时,这个View就会从第一个Activity的显示状态过渡到第二个Activity的显示状态,给用户的感觉仿佛是两个Activity共享一个View.
代码可以参考最下方的github,我们先看下效果:
矢量动画
不知道大家对矢量图有没有了解,矢量图不同于位图是用像素描述图像的,它是用数学曲线描述图形。所以一张图片就是对应着一系列的数学曲线,所以图片的显示尺寸和图片体积无关。它的体积就是文本文件的大小。并且矢量图可以无限拉伸不失真。SVG就是标准的矢量图格式,Android中使用矢量图虽然没有直接使用SVG图片,但是Google提供了一个<vector>
标签用来创建VectorDrawable
矢量对象,我们只要把svg的path
标签中的字符串复制到的path
标签中,就能创建出一个一模一样的矢量图形。在AndroidStudio中可以通过导入SVG来在drawable 文件夹下生成相应的矢量图xml 文件进行引用,同时AndroidStudio本身也提供一些矢量图库供开发者使用。除了AndroidStudio本身提供的库,开发者也可以去阿里巴巴矢量图标库去搜寻下载自己想要的矢量图标。打开SVG文件或者vector 标签,我们可以看到path标签中的一串数字,这其实就是用来绘制svg图像,而path标签中那些M、C、Z等命令其实就是绘制svg图像时的规则,我们来看看svg图像各个命令的意思:
M = moveto 相当于 android Path 里的moveTo(),用于移动起始点
L = lineto 相当于 android Path 里的lineTo(),用于画线
H = horizontal lineto 用于画水平线
V = vertical lineto 用于画竖直线
C = curveto 相当于cubicTo(),三次贝塞尔曲线
S = smooth curveto 同样三次贝塞尔曲线,更平滑
Q = quadratic Belzier curve quadTo(),二次贝塞尔曲线
T = smooth quadratic Belzier curveto 同样二次贝塞尔曲线,更平滑
A = elliptical Arc 相当于arcTo(),用于画弧
Z = closepath 相当于closeTo(),关闭path
简单介绍了下Android 下的矢量图,我们继续讲矢量动画。Android中的矢量动画看似很繁杂,其实很简单,就三个类:vector、animated-vector、animated-selector
(1)vector:显示一个矢量图形,用SVG的语法构建path
(2)animated-vector:组合两个vector,让vector动起来
(3)animated-selector:组合两个animated-vector,实现双向切换动画
三个类的递进关系很明显。
ok,下面我们用代码实现一个矢量动画:
首先在ImageView中引用 animvectordrawable
<ImageView
android:id="@+id/iv_vector"
android:layout_width="200dp"
android:layout_height="200dp"
android:src="@drawable/animvectordrawable" />
我们看下animvectordrawable.xml的代码:
<!--drawable 初始显示的样子-->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:drawable="@drawable/vectordrawable"
tools:ignore="NewApi">
<!--指定旋转动画-->
<target
android:name="rotationGroup"
android:animation="@anim/rotation" />
<!--指定path转变动画-->
<target
android:name="v"
android:animation="@anim/path_morph" />
</animated-vector>
animated-vector
标签:动态的Vector需要通过animated-vector标签来进行实现,它就像一个粘合剂,将控件与Vector图像粘合在了一起,而且animated-vector规定,可以有多个动画同时进行,但是一个对象上只能加载一个动画。
我们这时候看下vectordrawable.xml 的代码:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="300dp"
android:height="300dp"
android:viewportHeight="70"
android:viewportWidth="70">
<!--
我们要实现旋转 加 路径变换的动画,所以两个动画不能同时放在一个对象上,所以必须用group包一层,把path变换动画放在path对象上,把旋转动画放在group对象上,从而实现整体的效果。
-->
<group
android:name="rotationGroup"
android:pivotX="35"
android:pivotY="35"
android:rotation="0.0">
<path
android:name="v"
android:fillColor="#000000"
android:pathData="M10,10 L60,10 L60,20 L10,20 Z M10,30 L60,30 L60,40 L10,40 Z M10,50 L60,50 L60,60 L10,60 Z" />
</group>
</vector>
我们在看下target 指定的两个动画:rotation.xml和path_morph.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!--属性动画,旋转360度-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1500"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360" />
</set>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!--
一个path变换到另一个path
valueFrom指定变换前的path,valueTo指定变换后的path,propertyName和valueType在这个例子中是固定写法
-->
<objectAnimator
android:duration="1500"
android:propertyName="pathData"
android:valueFrom="M10,10 L60,10 L60,20 L10,20 Z M10,30 L60,30 L60,40 L10,40 Z M10,50 L60,50 L60,60 L10,60 Z"
android:valueTo="M5,35 L40,0 L47.072,7.072 L12.072,42.072 Z M10,30 L60,30 L60,40 L10,40 Z M12.072,27.928 L47.072,62.928 L40,70 L5,35 Z"
android:valueType="pathType" />
</set>
valueFrom
和valueTo
分别指定了变换前的path和变换后的path,它要求前后两个path必须是同形path
(PS:如果两个path拥有相同的命令数,并且对应位置的命令符相同,那么这两个path我们称之为同形path。如:
M10,15 L20,20 L25,15 C10,20,20,20,30,10 L50,50 Z
M20,30 L10,10 L15,25 C25,10,30,30,10,20 L35,35 Z)
下面由java 代码进行启动就可以了:
Drawable drawable = ivVector.getDrawable();
((Animatable) drawable).start();
OK,下面看下效果图:
简单小技巧:
是不是觉得很麻烦,我刚开始理解的时候也是看了一段时间才弄懂。上面的逻辑弄懂以后,教给大家一个简单的方法,能够不敲代码就能实现矢量动画例子。之所以放到后面才讲,是希望大家先看明白上面的逻辑,否则的话不知其所以然其实对自己并没有好处。
首先从Android Studio 的 Vector Drawable 下载一个哭脸矢量图,
然后打开在线工具 这个网站,选择左下角的 import -> Vector Drawable,导入哭脸的图形。
导入完后,看到效果如下:
然后,点击左下角的那个加号按钮,并选择“New group layer”
然后把三个path 拖到group 里,有没有种熟悉感,其实这就相当于咱们 上面中vectordrawable.xml 代码中path 外套一层group。
一切准备就绪后,我们需要对 group 做旋转动画,对嘴型的 path(也就是 path_2)做路径转换动画。
那么首先设置 group 的旋转中心,点击 group 层,然后在右上角设置旋转的中心,设置后效果如下:
然后,点击 group 层最右边的类似小闹钟按钮,选择 rotation,然后在右上角设置选择参数,效果如下:
从截图中的右下方可以看到动画的时间轴,总时间是 300 ms,所以我在右上角中设置 endtime 为 300,并设置 tovalue 为360。到这里,< group > 的旋转效果就设置好,当你点击那个播放按钮的时候,就会看到旋转动画
然后为嘴型那个 path(也就是 path_2 层)设置矢量图动画,点击 path_2 层的右边的类似小闹钟按钮,然后选择 pathData,然后同上在右上角看到设置pathData数据
endTime 同样设置为了 300,那么这个 toValue 是关键,决定最终效果,刚刚我们不是从 Android Studio 下载了个笑脸矢量图嘛,那么这个就设置笑脸图形中的那个嘴型 Path。
设置完成后,你可以再点击播放按钮,这时候就得到了我们想要的动画。接下来要导出来,点击左下角的 “export” ,然后选择 “Animated Vector Drawable”
把这个导出文件放到 res/drawable 目录下,然后给上面例子中的 ImageView 设置 android:src ,最后再在代码中引用,就实现了一个矢量动画。
效果如下:
下面我们在说一下animated-selector:是用于两个状态的切换,在两个path之间来回切换显示。看一下效果:
最关键的代码如下:
<?xml version="1.0" encoding="utf-8"?>
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="NewApi">
<!--指定选择状态下的图片 而且一定要是选择状态下在前面-->
<item
android:id="@+id/state_on"
android:drawable="@drawable/ic_twitter"
android:state_checked="true" />
<!--指定平常状态下的图片-->
<item
android:id="@+id/state_off"
android:drawable="@drawable/ic_heart" />
<!-- 指定从平常状态到选择状态下的矢量图动画-->
<transition
android:drawable="@drawable/heart_to_twitter"
android:fromId="@id/state_off"
android:toId="@id/state_on" />
<!-- 指定从选择状态到平常状态下的矢量图动画-->
<transition
android:drawable="@drawable/twitter_to_heart"
android:fromId="@id/state_on"
android:toId="@id/state_off" />
</animated-selector>
其他的内容就不讲解了,篇幅已经很长了,具体的实现可以看github。
其他的动画效果
最后,再给大家展示一下github 上的其他的动画效果,而且代码上都有详细的注释,很容易理解。
总结
以上就是对最近实现Android 动画效果的一个总结,在期间也看了很多大神的博客,学习到了很多的知识,特别感谢 Android高级动画-大公爵、Android:获取并制作矢量图动画-Mr13_周、十分钟搞定酷炫动画,Android自定义 View 入门-Anonymous___、Android 自定义View高级特效,神奇的贝塞尔曲线-一口仨馍以及转化同形Path的工具。可以说没有上面这些大神的博客就没有总结。
最后的最后,附上所有动画效果的代码https://github.com/smile-sxl/AdvanceAnimation。如果感觉对您有帮助,请在GitHub 上 star 一下,谢谢您的支持。