文章主要内容来源《Android开发艺术探索》,部分内容来源网上的文章,文中会有链接。
Android系统提供了两个动画框架:属性动画框架和View动画框架。 两个动画框架都是可行的选项,但是属性动画框架通常是首选的使用方法,因为它更灵活,并提供更多的功能。 除了这两个框架,还可以使用Drawable动画(即逐帧动画,AnimationDrawable),它允许你加载Drawable资源并逐帧地显示它们。
View动画框架(补间动画)
View动画框架中一共提供了AlphaAnimation(透明度动画)、RotateAnimation(旋转动画)、ScaleAnimation(缩放动画)、TranslateAnimation(平移动画)四种类型的补间动画;并且View动画框架还提供了动画集合类(AnimationSet),通过动画集合类(AnimationSet)可以将多个补间动画以组合的形式显示出来。补间动画的实现,一般会采用xml文件的形式,那样代码会更容易书写和阅读,同时也更容易复用。属性动画框架
与属性动画相比View动画存在一个缺陷,View动画改变的只是View的显示,而没有改变View的响应区域,并且View动画只能对View做四种类型的补间动画。因此Google在Android3.0(API级别11)及其后续版本中添加了属性动画框架,从名称中就可以知道只要某个类具有属性(即该类含有某个字段的set和get方法),那么属性动画框架就可以对该类的对象进行动画操作(其实就是通过反射技术来获取和执行属性的get,set方法),同样属性动画框架还提供了动画集合类(AnimatorSet),通过动画集合类(AnimatorSet)可以将多个属性动画以组合的形式显示出来。作者:ForeverCy
链接:http://www.jianshu.com/p/b117c974deaf
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
一、View动画
View动画的四种变换效果对应着Animation的四个子类:TranslateAnimation、ScaleAnimation、RotateAnimation、AlphaAnimation。四种动画可以用这四个类通过代码实现,也可以使用XML文件来定义。建议使用XML来定义动画,因为XML格式可读性好而且方便复用。
名称 | 标签 | 子类 |
---|---|---|
平移动画 | TranslateAnimation | |
缩放动画 | ScaleAnimation | |
选中动画 | RotateAnimation | |
透明度动画 | AlphaAnimation |
View动画可以是单个动画,也可以由一系列动画组成,使用标签实现组合。标签对应AnimationSet类,内部也可以嵌套其他动画集合。
android:interpolator表示动画集合所使用的插值器,插值器影响动画的速度,比如非匀速动画就需要通过插值器来控制动画的播放过程。这个属性可以不指定,默认是@android:anim/accelerate_decelerate_interpolator,即加速减速插值器。
android:shareInterpolator表示集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,那么子动画就需要单独指定所需要的插值器或者使用默认值。
android:fromXDelta x的起始值
android:toXDelta x的结束值
android:fromYDelta y的起始值
android:toYDelta y的结束值
android:fromXScale 水平方向缩放的起始值
android:toXScale 水平方向缩放的结束值
android:fromYScale 竖直方向缩放的起始值
android:toYScale 竖直方向缩放的结束值
android:pivotX 缩放的轴点的x坐标
android:pivotY 缩放的轴点的y坐标
android:fromDegrees 旋转开始的角度
android:toDegrees 旋转结束的角度
android:pivotX 旋转的轴点的x坐标
android:pivotY 选中的轴点的y坐标
android:fromAlpha 透明度的起始值
android:toAlpha 透明度的结束值
一些其他常用属性
android:duration 动画的持续时间
android:fillBefore 动画结束后是否回到执行前的位置
android:fillAfter 动画结束后是否停留在结束位置
android:interpolator 动画使用的插值器
android:startOffset 动画执行之前的等待时间
android:repeatCount 动画重复执行的次数
xml属性都有对应的set方法
使用示例
首先编写动画xml文件test_anim.xml
Button mButton = (Button)findViewById(R.id.button);
Animation animation = AnimationUtils.loadAnimation(context, R.anim.test_anim);
mButton.startAnimation(animation);
也可以通过代码实现
AlphaAnimation animation = new AlphaAnimation(0, 1);
animation.setDuration(300);
mButton.startAnimation(animation);
使用setAnimationListener方法可以给动画添加过程监听。
View动画的特殊使用场景
LayoutAnimation控制ViewGroup的子View的出场效果,使用步骤如下:
- 定义LayoutAnimation,文件名res/anim/anim_layout.xml
android:delay 子元素开始动画的时间延迟
android:animationOrder 子元素动画的顺序。有normal(顺序)、reverse(逆向)、random(随机)三种顺序。
android:animation 子元素入场的具体动画
- 定义子元素具体的入场动画,文件名res/anim/anim_item.xml
- 为Viewgroup指定 android:layoutAnimation属性,比如ListView:
除了通过xml中指定layoutAnimation属性,也可以通过LayoutAnimationController来实现:
Animation animation = AnimationUtils.loadAnimation(context, R.anim.anim_item);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
listView.setLayoutAnimation(controller);
Activity的切换效果
使用overridePendingTransition(int enterAnim, int exitAnim)方法实现Activity的切换动画,这个方法必须在startActivity()或者finish()之后调用才能生效。
Fragment也可以添加切换动画,通过FragmentTransaction中的setCustomAnimations()方法实现。
二、帧动画
帧动画的标签是,对应AnimationDrawable类。
示例代码
Button mButton = (Button)findViewById(R.id.button1);
mButton.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable drawable = (AnimationDrawable)mButton.getBackground();
drawable.start();
注意帧动画容易引起OOM。
三、属性动画
属性动画从 API 11 才有,属性动画可以对任意对象的属性进行动画而不仅仅是View,达到的效果是:在一个时间间隔内完成对象从一个属性值到另一个属性值的改变。属性动画常用的动画类:ValueAnimator、ObjectAnimator、AnimatorSet。ObjectAnimator继承自ValueAnimator,AnimatorSet是动画集合。
属性动画也有对应的xml标签,但是建议使用代码来实现属性动画,因为使用代码比xml简单。而且很多时候一个属性的起始值无法提前确定。
1、通过几个例子看如何使用属性动画:
- 改变对象myObject的translationY属性,让其沿着Y轴向上平移一段距离:它的高度。该动画在默认时间内完成
ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).start();
- 改变一个对象的背景色属性,让背景色在3秒内从 0xFFFF8080 到 0xFF8080FF 的渐变,动画会无限循环而且会有反转效果
ValueAnimator anim = ObjectAnimator.ofInt(myObject, "backgroundColor",0xFFFF8080,0xFF8080FF);
anim.setDuration(3000);
anim.setEvaluator(new ArgbEvaluator());
anim.setRepeatCount(ValueAnimator.INFINITE);
anim.setRepeatMode(ValueAnimator.REVERSE);
anim.start();
- 动画集合,5秒内对View的旋转,平移,缩放和透明都进行改变。
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(myView, "rotationX", 0, 360),
ObjectAnimator.ofFloat(myView, "rotationY", 0, 180),
ObjectAnimator.ofFloat(myView, "rotation", 0, -90),
ObjectAnimator.ofFloat(myView, "translationX", 0, 90),
ObjectAnimator.ofFloat(myView, "translationY", 0, 90),
ObjectAnimator.ofFloat(myView, "scaleX", 1, 1.5f),
ObjectAnimator.ofFloat(myView, "scaleY", 1, 0.5f),
ObjectAnimator.ofFloat(myView, "alpha", 1, 0.25f, 1)
);
set.setDuration(5*1000).start();
2、上面的代码总结
上面代码用到一个主要的方法
ObjectAnimator.ofFloat(target, propertyName, values...);
target 是动画目标,任何对象,不一定必须是View
propertyName 是属性名字
values 是可变参数, 从v1变化到v2到vn。。。
例子:
mIv是一个imageView
//alpha 从0 到1 的动画
ObjectAnimator.ofFloat(mIv, "alpha", 0f,1f)
.setDuration(500)
.start();
如果要实现其他效果,修改propertyName和values就行了。
属性动画的原理就是通过反射,以动画的效果多次调用set方法来改变属性值的。所以,使用属性动画时,相应的对象属性必须有set方法,get方法可以没有,但是如果使用动画的时候没有传递初始值,就必须提供get方法,因为系统要通过get方法获取属性的默认初始值。
在属性动画中,View的常用属性
alpha 透明度
rotation z轴旋转
rotationX x轴旋转
rotationY y轴旋转
translationX x水平偏移
translationY y水平偏移
ScaleX x轴缩放
ScaleY y轴缩放
3、插值器和估值器
插值器(TimeInterpolator/Interpolator)用来修饰动画效果,定义动画的变化规率(变化趋势),比如平移动画,可以匀速平移也可以加速平移,这个由插值器决定。
估值器(Evaluator)用来决定具体的数值变化,比如(匀)加速平移时,“加速度”是多少由估值器决定。
插值器TimeInterpolator和Interpolator,后者是继承前者的接口。
TimeInterpolator接口是属性动画中新增的,用于兼容Interpolator接口,这使得所有过去的Interpolator实现类都可以直接在属性动画使用。
出自:http://blog.csdn.net/carson_ho/article/details/72863901
系统内置的插值器有以下几种:
- AccelerateInterpolator 加速
- DecelerateInterpolator 减速
- LinearInterpolator 匀速
- AccelerateDecelerateInterpolator 先加速再减速(在动画开始与结束的地方速率改变比较慢,在中间的时候最快)
- AnticipateInterpolator 开始的时候向后然后向前甩(先退后再加速前进)
- AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值(先退后再加速前进,超出终点后再回终点)
- BounceInterpolator 动画结束的时候弹起(最后阶段弹球效果)
- CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
- OvershootInterpolator 向前甩一定值后再回到原来位置(快速完成动画,超出再回到结束时的位置)
以上每种插值器都有对应的xml资源,比如:@android:anim/linear_interpolator。(注意资源名的规律)
系统内置的估值器有以下三种:
IntEvaluator 以整型的形式从初始值到结束值 进行过渡
FloatEvaluator 以浮点型的形式从初始值到结束值 进行过渡
ArgbEvaluator 以Argb类型的形式从初始值到结束值 进行过渡
如果系统内置的插值器和估值器无法满足需求,也可以自定义。
View动画的插值器实现Interpolator接口,View动画没有估值器
属性动画的插值器实现实现TimeInterpolator接口,估值器实现TypeEvaluator接口
自定义插值器和估值器参考系统内置的插值器和估值器即可。
以下摘自http://blog.csdn.net/carson_ho/article/details/72863901
实现Interpolator接口自定义插值器的说明(TimeInterpolator接口相同)
public interface Interpolator {
// 内部只有一个方法
float getInterpolation(float input) {
// 参数说明
// input值值变化范围是0-1,且随着动画进度(0% - 100% )均匀变化
// 即动画开始时,input值 = 0;动画结束时input = 1
// 而中间的值则是随着动画的进度(0% - 100%)在0到1之间均匀增加
...// 插值器的计算逻辑
return xxx;
// 返回的值就是用于估值器继续计算的fraction值
}
}
实现TypeEvaluator接口自定义估值器的说明
public interface TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 参数说明
// fraction:插值器getInterpolation()的返回值
// startValue:动画的初始值
// endValue:动画的结束值
....// 估值器的计算逻辑
return xxx;
// 赋给动画属性的具体数值
// 使用反射机制改变属性变化
// 特别注意
// 插值器的input值 和 估值器fraction有什么关系呢?
// 答:input的值决定了fraction的值:input值经过计算后传入到插值器的getInterpolation()
// 然后通过实现getInterpolation()中的逻辑算法,根据input值来计算出一个返回值,而这个返回值就是fraction了
}
}
如何使用插值器和估值器
View动画和属性动画都可以使用插值器,估值器只有属性动画可以用。
插值器用法
// 步骤1:创建需要设置动画的视图View
Button mButton = (Button) findViewById(R.id.Button);
// 步骤2:创建透明度动画的对象 & 设置动画效果
Animation alphaAnimation = new AlphaAnimation(1,0);
alphaAnimation.setDuration(3000);
// 步骤3:创建对应的插值器类对象
Interpolator overshootInterpolator = new OvershootInterpolator();
// 步骤4:给动画设置插值器
alphaAnimation.setInterpolator(overshootInterpolator);
// 步骤5:播放动画
mButton.startAnimation(alphaAnimation);
估值器用法
// 在第3个参数中传入对应估值器类的对象
ObjectAnimator anim = ObjectAnimator.ofObject(myView2, "height", new Evaluator(),1,3);
4、属性动画的监听
有两个监听接口AnimatorListener和AnimatorUpdateListener。AnimatorListener和View动画的AnimationListener类似,可以监听动画的开始、结束等过程。AnimatorUpdateListener可以监听动画的每一帧变化,动画每变化一帧,接口里的方法被调用一次。
四、动画相关的其他问题、知识、技巧
对于没有set和get方法的对象如何使用属性动画:
- 给对象加上相应的set和get方法,如果有权限的话(对系统的View是没有权限改源码的)
- 用一个包装类包装原始对象,间接的提供set和get方法
- 使用ValueAnimator,监听动画过程,自己实现属性的改变。ValueAnimator本身不作用于任何对象,直接使用它是没有效果的。它可以对一个值做动画,可以监听其动画过程,在动画过程中自己实现修改对象的属性值,也就相当于对象做了动画,示例代码如下:
private void performAnimate(final View button, final int start, final int end) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
//持有一个IntEvaluator对象,方便下面估值的时候使用
private IntEvaluator mIntEvaluator = new IntEvaluator();
@Override
public void onAnimationUpdate(ValueAnimator animator) {
//获得当前动画的进度值,整型1-100之间
int currentValue = (int) animator.getAnimatedValue();
//获得当前进度占整个动画过程的比例,浮点型,0-1之间
float fraction = animator.getAnimatedFraction();
//直接调用整型估值器,通过比例计算出宽度,然后设给Button
button.getLayoutParams().width = mIntEvaluator.evaluate(fraction, start, end);
button.requestLayout();
}
});
valueAnimator.setDuration(5000).start();
}
@Override
public void onClick(View v) {
if (v == mButton) {
performAnimate(mButton, mButton.getWidth(), 500);
}
}
通过源码可以看出,属性动画需要运行在有Looper的线程中
使用动画的注意事项
- OOM问题。主要是帧动画图片数量较多且图片较大时容易出现OOM。
- 内存泄漏。在属性动画中有一类无限循环的动画,这类动画需要在Activity退出时及时停止,否则将导致Activity无法释放从而造成内存泄漏。通过验证后发现View动画不存在此问题。
- 兼容性问题。动画在3.0以下的系统上有兼容性问题,需要做好适配。
- View动画问题。View动画是对View的影像做动画,并不会真正的改变View的状态。如果出现动画完成后View无法隐藏,即setVisibility(View.GONE)失效,这个时候只要调用view.clearAnimation()清除View动画即可解决。
- 不要使用px。尽量使用dp,使用px会导致在不同设备上有不同的效果。
- View平移后:在3.0以前的系统,View动画和属性动画都是新位置无法点击,老位置可以点击;3.0以后(包括3.0),属性动画的点击事件触发位置是位移后的位置,View动画是原位置。
- 开启硬件加速可以提高动画的流畅性。
五、补充属性动画
View动画可以设置ViewGroup的子View的出场动画,属性动画可以为ViewGroup的子View的显示和隐藏设置过渡动画。出场动画文中已经介绍,属性动画实现的过度动画详细见ForeverCy的文章Android中的View动画和属性动画 中的相关部分。