谈谈动画
动画作为展现动态效果不可缺少的一环,还是异常重要的,其主要运用在View中。
按照Android发展史可以分成三个大类:逐帧动画、补间动画、属性动画。
在现在的实际开发里面,使用很多的还是属性动画相关的东西,前两种动画已经不大常用,渐渐淡出了人们的视野。在这里只是简单介绍。
逐帧动画
这种原理就类似于gif图,播放所有已有的帧,通过人眼的短暂视觉残留来完成动画。
等价于Android里面的AnimationDrawable
利用XML进行定义
需要在 <animation-list .../> 标签下使用 <item .../> 子元素标签定义动画的全部帧(引入每一帧对应的资源文件),并指定各帧的持续时间。以完成目的。
该文件创建在res/drawable文件目录之下
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true|false">
<item android:drawable="xxx" android:duration="xx"/>
<!-- more... -->
</animation-list>
其中oneshot表示该动画是否循环播放,true表示只播放一次,false表示无限循环。
之后在ImageView里面设置对应的资源即可。
只需要获取到对应的AnimationDrawable对象,即可通过start()以及stop()方法来控制动画的开始与停止。
直接使用代码定义
初始化AnimationDrawable对象后,通过addFrame(Drawable,int)来进行加入,分别代表对应帧和持续时间。
直接在ImageView里面setDrawable()来进行绑定。
setOneShot(true)设置动画是都循环播放。
同样的,使用start()以及stop()方法来控制动画的开始与停止。
使用太多较大的图片容易引起OOM
补间动画
顾名思义,就是开发者(我们)只需要给出动画的第一帧以及最后一帧,通过系统自动实现中间的过渡动作。
Android帮提供了以下的几种属性的动画
- 透明度变换:
Alpha - 位移:
translate - 缩放:
scale - 旋转:
rotate
可以通过XML文件对他们进行设置,在代码之中分别对应AlphaAnimation/TranslateAnimation/ScaleAnimation/RotateAnimation这四个类,共同父类为Animation
具体的不会过多展开讲解,可参考该博客
利用XML进行定义
需要将对应的资源文件放于目录res/anim文件下
一些属性:
-
duration持续时间 -
interpolator插值器,控制动画的变化速率。在
Android里面自带以下实现:-
AccelerateDecelerateInterpolator
在动画开始与结束的地方速率改变比较慢,在中间的时候加速
-
AccelerateInterpolator
在动画开始的地方速率改变比较慢,然后开始加速
-
AnticipateInterpolator
开始的时候进行反向效果然后按照预期效果进行
-
AnticipateOvershootInterpolator
开始的时候进行反向效果然后超出预期值后返回最后需求的状态
-
BounceInterpolator
动画结束的时候会重复最后一小段时间内的动画效果
-
CycleInterpolator
动画循环播放特定的次数,速率的大小改变遵从正弦曲线
-
DecelerateInterpolator
在动画开始快然后速率减小
-
LinearInterpolator
匀速改变
-
OvershootInterpolator
超出预期值后再回到最后的状态
这些插值器都能够通过
@android.anim/xxx进行引用。
-
-
fillAfter动画结束后是否停留在结束位
而在对应的xml属性之中,通过设定fromXxx和toXxx属性来设定对应属性的开始值和终点值。
以移动为例:
<translate
android:fromXDelta="0"
android:toXDelta="100"
android:fromYDelta="0"
android:toYDelta="100" />
X和Y对应的是X方向与Y方向的操作。
其中对于透明度的设置范围是-1.0-1.0
缩放值跟的是相对应的倍数,例如0.5是缩小一半,2.0是放大一倍
然后在rotate以及scale范围里面还有两个参数pivotX/pivotY来表示操作的中心点(起始点)
对应的有几种表示方式,以pivotX为例子讲解
10:动画开始X方向的点为View左上角的点 在x方向 加上 10像素的位置10%: 动画开始X方向的点View左上角的点 在x方向 加上 自身宽度乘上10%的位置10%p:动画开始X方向的点离View左上角的点 在x方向 加上 父控件宽度乘上10%数值的位置
利用Java实现补间动画
该方法通过实例化对应的动画对象来进行相对应的设置。
这些动画的使用都是调用View.startAnimation(anim)开始动画
通过可以通过setDuration(int)方法设置动画运行的时间
translate
Animation translateAnimation = new TranslateAnimation(0,500,0,500);
/**
* 1. fromXDelta :视图在水平方向x 移动的起始值
* 2. toXDelta :视图在水平方向x 移动的结束值
* 3. fromYDelta :视图在竖直方向y 移动的起始值
* 4. toYDelta:视图在竖直方向y 移动的结束值
*/
scale
Animation scaleAnimation= new ScaleAnimation(0,2,0,2,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
/**
* 1. fromX :动画在水平方向X的结束缩放倍数
* 2. toX :动画在水平方向X的结束缩放倍数
* 3. fromY :动画开始前在竖直方向Y的起始缩放倍数
* 4. toY:动画在竖直方向Y的结束缩放倍数
* 5. pivotXType:缩放轴点的x坐标的模式
* 6. pivotXValue:缩放轴点x坐标的相对值
* 7. pivotYType:缩放轴点的y坐标的模式
* 8. pivotYValue:缩放轴点y坐标的相对值
* pivotXType = Animation.ABSOLUTE:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
* pivotXType = Animation.RELATIVE_TO_SELF:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
* pivotXType = Animation.RELATIVE_TO_PARENT:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)
*/
Rotate
Animation rotateAnimation = new RotateAnimation(0,270,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
/**
* 1. fromDegrees :动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
* 2. toDegrees :动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
* 3. pivotXType:旋转轴点的x坐标的模式
* 4. pivotXValue:旋转轴点x坐标的相对值
* 5. pivotYType:旋转轴点的y坐标的模式
* 6. pivotYValue:旋转轴点y坐标的相对值
* pivotXType具体值以及作用同上
*/
alpha
Animation alphaAnimation = new AlphaAnimation(1,0);
// 1. fromAlpha:动画开始时视图的透明度(取值范围: -1 ~ 1)
// 2. toAlpha:动画结束时视图的透明度(取值范围: -1 ~ 1)
动画的设置优先级:子动画>动画集。即,子动画设置了无限循环,动画集设置了只运行一次,运行的时候还是无限循环的模式。
当然,也可以通过自己需求对补间动画进行定义,继承Animation类,并重写applyTranformation()方法
具体的就不详细讲述了,因为:
我们为什么不用属性动画呢?
属性动画
属性动画直白来说,是对任意对象的任意属性进行动画过渡,是补间动画的加强版 其优点:
- 针对所有的对象
- 改变了
View的属性值,在动画的时候已经对应改变了view的属性(响应点击事件) - 可扩展性(自定义属性效果)
我们通常使用以下两个类
-
ValueAnimator在属性动画里面使用到的时间引擎,计算对应的属性值
-
ObjectAnimator是上者的子类,对指定对象的属性执行动画
为了健壮性,一般是在代码里面声明对象,在这里就不介绍XML设置的方式
ValueAnimator
通过不断控制值的变化,在不断手动赋值给对象的属性,从而实现动画效果。
过程:
-
设置动画的运行时长,动画效果以及开始和结束的属性值
-
设置估值器
该类描述动画如何从初始值过渡到结束值的逻辑
-
添加
AnimatorUpdateListener,该接口会直接回调当前状态的Animation,通过animation.getAnimatedValue()(记得强制转换,因为该方法返回的是Object)获取到当前的值 -
将值设置到给需要进行变换的属性上面
-
通知
View进行重绘postInvalidate()invalidate()
在这里面主要是使用ValueAnimator.ofXxx(...values)静态方法来获取相对应的ValueAnimator
含有ofInt()/ofFloat()/ofObject()/ofArgb()/ofPropertyValuesHolder()这几种方法
分别对应其中的过渡值:整形值、浮点值、对象、颜色值、存储属性的动画改变值
实际开发里面常用前面三种,在使用的时候传入对应属性的变化值
Animator animator = ValueAnimator.ofFloat(0, 3f);
// 设置运行时间
animator.setDuration((long) (time * 1000));
// 设置重复次数
animator.setRepeatCount(0);
// 设置重复播放动画模式
// ValueAnimator.RESTART(默认):正序重放
// ValueAnimator.REVERSE:倒序回放
animmator.setRepeatMode(ValueAnimator.RESTART);
// 设置插值器
animator.setInterpolator(new LinearInterpolator());
// 添加监听
animator.addUpdateListener(animation -> {
float f = (float) animation.getAnimatedValue();
// 对view进行操作
postInvalidate();
});
animator.start();
但是对于对象的过渡,我们需要自定义估值器(自定义类实现TypeEvaluator接口)
复写evaluate(float fraction, Object startValue, Object endValue)方法
分别对应:
- fraction:表示动画完成度(根据它来计算当前动画的值)
- startValue、endValue:动画的初始值和结束值
最后需要返回对象经过过渡逻辑计算后的值
本质上还是在操作值,不过将多个值全部封装到了一个对象里面
ObjectAnimator
直接传入对象以及对象的属性名以及操作值来实现动画效果
该类相比于ValueAnimator来说,只是少了赋值给对应属性这一步
新建对象与ValueAnimator相似,也是通过ofXxx方法,不过传入的参数有区别,加入了对应的新参数
(Object object, String property, ....values)
- 操作对象
- 操作属性
- 变化值
使用样例如下
ObjectAnimator animator = ObjectAnimator.ofFloat(this, "translationX", 0, 200f);
animator.setDuration((long) (actionTime * 1000));
animator.start();
对于ofPropertyValuesHolder()来说,只需要传入(Object,PropertyValuesHolder ... values)即可
PropertyValuesHolder
存放做动画的对应属性以及期间的变化值,声明方式与ValueAnimator类似,不过在之前需要传入属性名
对于该类,在最开始有一个简单解释:
此类包含有关属性的信息以及该属性在动画期间应采用的值。 PropertyValuesHolder对象可用于使用ValueAnimator或ObjectAnimator创建动画,这些动画可并行操作多个不同的属性。
可等价于动画集合AnimatorSet,在ObjectAnimator和ValueAnimator之中调用并传入参数即可。
PropertyValuesHolder upX = PropertyValuesHolder.ofFloat("translationX", 0, -20);
PropertyValuesHolder upY = PropertyValuesHolder.ofFloat("translationY", 0, -20);
ObjectAnimator animatorUp = ObjectAnimator.ofPropertyValuesHolder(this, upX, upY)
.setDuration((long) (actionTime * 1000));
animatorUp.setStartDelay((long) (actionTime * 100));
animatorUp.start();
动画监听
除了AnimatorUpdateListener来监听过程中的值,也能够通过添加AnimatorListener来实现动画过程的监听以完成交互
实现该接口需要重写以下方法:
-
onAnimationStart动画开始时
-
onAnimationRepeat动画重复时
-
onAnimationCancel动画取消时
-
onAnimationEnd动画结束时
在Animation中通过addListener方法传入。(动画对象都可以调用addListener方法来实现监听)
AnimatorUpdateListener只能够用于ValueAnimator之中(只有该类暴露了设置的接口)
假若重写四个方法太繁琐,可以使用AnimatorListenerAdapter来指定复写某方法
通过自定义对象属性来实现动画效果
前提:
- 为对象设置需要操作属性的
get以及set属性 以下方法针对没有直接的get与set方法的情况- 继承原始类,对外暴露该属性的
get与set - 用一个类来包装原始对象,进行扩展(设计模式--装饰模式)
- 继承原始类,对外暴露该属性的
- 通过
TypeEvaluator类实现属性变化的逻辑 - 调用
ofObjecct()方法
手动设置对应属性的时候,注意:
- 对外暴露对应的
set/get方法 - 对应的
set方法对该属性的改变会通过某种方式反映出来 例如view.setWidth()与view.getWidth()并不会完成预期效果
ViewPropertyAnimator
为了面向对象而新增的一个类,可以理解为是属性动画的一种简写方式。
通过View.animate().xxx().xxx()进行链式调用。
在animate()方法后返回了一个ViewPropertyAnimator对象,之后所有方法都是在这个基础上进行调用。
一个简单例子:
view.animate().alpha(0f);
// 单个动画设置:将按钮变成透明状态
view.animate().alpha(0f).setDuration(3 * 1000).setInterpolator(new BounceInterpolator());
// 单个动画效果设置以及参数设置
view.animate().alpha(0f).x(50).y(50);
/**
* 组合动画:将按钮变成透明状态再移动到(50,50)处
* 特别注意:
* 动画会自启动,无需调用start()方法.因为新的接口中使用了隐式启动动画的功能,只要我们将动画定义完成后,动画就会自动启动
* 该机制对于组合动画也同样有效,只要不断地连缀新的方法,那么动画就不会立刻执行,等到所有在ViewPropertyAnimator上设置的方法都执行完毕后,动画就会自启动
*/
AnimatorSet
承载一个动画集合,可以通过相关的方法调整展示的顺序。play()内部调用Builder()方法创建对应的对象。
例如:
AnimatorSet s = new AnimatorSet();
s.play(anim1).with(anim2);
s.play(anim2).before(anim3);
s.play(anim4).after(anim3);
在xml中使用<set ... />标签来结合多个Animation后在代码里面通过以下方式加载动画
// 加载动画资源
Animation anim = AnimationUtils.loadAnimation(this,R.anim.xxx);
//设置动画结束后保留结束状态
anim.setFillAfter(true);
// 之后通过ImageView里面的startAnimation进行动画的开始
ImageView im = findViewById(xxx);
im.startAnimation(anim);
其他动画
layoutAnimation
针对于ViewGroup里面的子元素,譬如说ListView
XML:<layoutanimation .../>- 代码:
LayoutAnimationController
在XML之中指定ViewGroup的layoutAnimation属性即可生效
Activity切换效果
使用overridePendingTransiton(int start,int exit),分别传入动画的id,在startActivity(intent)与finish()方法后生效
使用动画应该注意的
-
避免使用帧动画(
AnimationDrawable)防止图片过大、过多出现OOM的情况
-
防止内存泄漏:
Activity退出关闭时关闭无限循环的动画 -
使用
view动画后若要做可见性操作请先clearAAnimation()清除 -
尽量使用dp而不是px
-
元素交互:
在3.0后,属性动画的单击事件触发位置为移动后的位置,但是view动画还是在原来的位置
-
建议开启硬件加速
提高动画流畅性
附录:效果图
AccelerateDecelerateInterpolator |
AccelerateInterpolator |
AnticipateInterpolator |
AnticipateOvershootInterpolator |
BounceInterpolator |
CycleInterpolator |
DecelerateInterpolator |
LinearInterpolator |
OvershootInterpolator |
AccelerateDecelerateInterpolator
AccelerateInterpolator
AnticipateInterpolator
AnticipateOvershootInterpolator
BounceInterpolator
CycleInterpolator
DecelerateInterpolator
LinearInterpolator
OvershootInterpolator