动画笔记
作者:彭欢亮
时间:2018/1/7 星期日 上午 10:10:10
Android 动画的分类 :
一、帧动画
实现帧动画的两种方式
- 利用xml形式实现
在drawable目录下创建一个animation-list xml 文件,
1 | <?xml version="1.0" encoding="utf-8"?> |
MainActivity代码
1 | ImageView imageView = (ImageView) findViewById(R.id.iv); |
- 帧动画实现的第二种方式,利用代码实现 (使用场景:帧动画图片太多的情况下,建议使用此方法,不用在xml文件中一个个添加)
MainActivity
1 | ImageView imageView = (ImageView) findViewById(R.id.iv) |
其中 anim.setOneShot(boolean arg) 表示 :帧动画是否执行一次 true表示执行一次,false表示循环
二、补间动画
- 补间动画的分类:
补间动画又可以分为四种形式,分别是 alpha(淡入淡出),translate(位移),scale(缩放大小),rotate(旋转)。
补间动画实现方式:
- 利用xml实现
首先在res目录下创建一个anim文件夹,即我们xml实现补间动画都是在res/anim/目录下添加
alpha_anim.xml动画实现
1 | <?xml version="1.0" encoding="utf-8"?> |
scale_anim.xml动画实现
1 | <?xml version="1.0" encoding="utf-8"?> |
集合动画
set_anim.xml动画集合
1 | <?xml version="1.0" encoding="utf-8"?> |
MainActivity代码
1 | ImageView imageView = (ImageView) findViewById(R.id.iv); |
AnimationUtils是系统实现的动画工具类
- 补间动画通过代码实现
代码如下:
1 | public void alpha(View v){ |
三、 Android 属性动画(Property Animation)
属性动画相关的类:
- ObjectAnimator (动画的执行类)
- ValueAnimator (动画的执行类)
- AnimatorSet (用于控制一组动画的执行:线性,一起,每个动画的先后执行等)
- AnimatorInflater ( 用户加载属性动画的xml文件)
- TypeEvaluator (类型估值,主要用于设置动画操作属性的值)
- TimeInterpolator(时间插值)
属性动画与补间动画的区别:
补间动画有一个最大的缺陷,就是它只是改变了View的显示效果而已,并不会真正的改变View的属性。场景:如果要改变view的宽度,补间动画实现不了,而属性动画可以。
ObjectAnimator用法
ObjectAnimator提供了几个方法(ofInt、ofFloat、ofObject…),都是用来设置动画作用的元素、作用的属性、动画开始、结束、以及中间的任意个属性值。
- translationX、translationY:这两个属性作为一种增量来控制着View对象从它布局容器的左上角坐标开始的位置。
- rotation、rotationX、rotationY:这三个属性控制着View对象围绕它的支点进行2D和3D的旋转。
- scaleX和scaleY:这两个属性控制着View对象围绕它的支点进行2D缩放。
- pivotX和pivotY:这两个属性控制着View对象的支点位置,围绕这个支点进行旋转和缩放变换处理。默认情况下,该支点的位置就是View对象的中心点。
- alpha:它表示View对象的alpha透明度。
- x、y:这是两个简单的实用的属性,它描述了View对象在它的容器中最终的位置。
- BackgroundColor:改变背景颜色
注意 缩放、反转等都有中心点或者轴,默认中心缩放,和中间对称线为反转线
1 | ObjectAnimator.ofFloat(imageView,"rotationX",0.0f,360.f,90.0f,360.0f) |
PropertyValuesHolder:以先将动画属性和值暂时的存储起来,后一起执行,在有些时候可以使用替换掉AnimatorSet,减少代码量。
1 | PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1, 0, 0.5f); |
ObjectAnimator简单的用法介绍完毕下面开始介绍ValueAnimator
ValueAnimator介绍以及用法
ValueAnimator是整个属性动画中最核心的一个类,前面介绍的ObjectAnimator也是继承自ValueAnimator。通过前面对ObjectAnimator的介绍,我们知道属性动画的实现机制是通过不断的地对View属性进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类负责计算的。
它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮助我们完成从初始值平滑过渡到结束值这样的效果。
ValueAnimator本身不提供任何动画效果,它更像一个数值发生器,用来产生具有一定规律的数字,从而让调用者来控制动画的实现过程。通常情况下,在ValueAnimator的AnimatorUpdateListener中监听数值的变化,从而完成动画的切换。
实现一个计时器效果
1 | private void startTimeClock() { |
实现一个物体自由落体
1 | public void verticalRun(View view) { |
动画的监听事件
- animator.addListener(AnimatorListener、AnimatorUpdateListener 对象)
实现AnimatorLisener对象
1 | animator.addListener(new Animator.AnimatorListener() { |
实现AnimatorUpdateListener对象
1 | animator.addListener(new AnimatorListenerAdapter() { |
实现AnimatorLisener、AnimatorUpdateListener 区别在于:前面这个监听需要实现所有的方法,而后面这个只需要实现其中一种方法即可。
- animator.addUpdateListener(AnimatorUpdateListener)
上面有个计时器的例子,可以通过实现的方法onAnimationUpdate(ValueAnimator animation)拿到相应的对象
AnimatorSet介绍与使用
介绍:动画的集合,在补间动画中也有个一个动画的集合类AnimationSet,所以AnimatorSet同样的效果,可以帮助我们实现组合属性动画的效果。
AnimatorSet这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ObjectAnimator或者ValueAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包含了以下四个方法:
- after(Animator anim) : 将现有动画插入到传入的动画之后执行。
- after(long delay):将现有的动画延迟指定的毫秒后执行。
- before(Animator anim):将现有的动画插入到传入的动画之前执行。
- with(Animator anim):将现有的动画和传入的动画同时执行。
例如我们想让一个TextView先从屏幕外移动到屏幕内,然后旋转360度,同时旋转过程中进行淡入淡出的效果,就可以写出如下代码:
1 | private void multiAnimator() { |
Animator监听器
一个完整的动画具有start、Repeat、End、Cancel四个过程,Android提供了接口,让我们能够很容易监听到这些事件。示例代码如下:
1 | animatorSet.addListener(new Animator.AnimatorListener() { |
使用xml文件创建属性动画
- 在res下创建一个animator的文件夹,即res/animator/.xml
例如:sacleX.xml
1 | <?xml version="1.0" encoding="utf-8"?> |
- 使用AnimatorInflater来加载动画的资源文件,然后设置给相应的target即可。
MainActivity.java 代码如下:
1 | public void loadXml(View view) { |
其它类似一致,下面讲解利用xml实现属性动画集合
animator_set
1 | <?xml version="1.0" encoding="utf-8"?> |
使用set标签,有一个orderring属性设置为together,还有另一个值:sequentially(表示一个接一个执行)。
MainActivity.java代码如下
1 | Animator animator = AnimatorInflater.loadAnimator(this, R.animator.scalex); |
代码同上。
布局动画
主要使用LayoutTransition为布局的容器设置动画,当容器中的视图层次发生变化时存在过渡的动画效果。(略)
View的anim方法
1 | mBlueBall.animate().alpha(0) |
属性动画原理
属性动画要求动画作用的对象提供该属性的get和set方法,属性动画根据你传递的该熟悉的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终值。如果让属性动画生效,要同时满足两个条件:
- object必须要提供setXxx方法,如果动画的时候没有传递初始值,那么还要提供getXxx方法,因为系统要去拿xxx属性的初始值(如果这条不满足,程序直接Crash)
- object的setXxx对属性xxx所做的改变必须能够通过某种方法反映出来,比如会带来ui的改变啥的(如果这条不满足,动画无效果但不会Crash)
属性动画为什么不能改变Button的宽高
因为Button的内部并没有自己的getWith()、setWidth()方法,为什么这样说,button继承至TextView,持有TextView的get、set方法。所以我们对Button做宽高的改变是,没有 效果的。
- 解决方法 1 : 用一个类来包装原始对象,间接为其提供get和set方法
包装类如下:
1 | public class ViewWrapper { |
MainActivity.java 代码:
1 | ViewWrapper viewWrapper = new ViewWrapper(mBtn); |
- 解决方法2: 采用ValueAnimator,监听动画过程,自己实现属性的改变
例子:
1 | private void performAnimate(final View mTarget, final int start, final int end) { |
- 用ValueAnimator来做动画。ValueAnimator并不会实质的做什么。所以需要后面的AnimatorUpdateListener来做一些粗活儿。这里指定的从1到100也没有什么实质的作用。并不是把按钮的宽度从1变到100。后面的代码很清晰的表达了这一点。
- 据这个fraction的百分比来计算当前时间片对应的按钮宽度是多少。当前宽度 = 初始宽度 + fraction * (结束宽度 - 初始宽度)。