1. android动画分为view动画、帧动画和属性动画,属性动画是API 11(Android 3.0)的新特性,帧动画一般也认为是view动画
1.1 view动画
- 平移动画 TranslateAnimation
- 缩放动画 ScaleAnimation
- 旋转动画 RotateAnimation
- 透明度动画 AlphaAnimation
AnimationSet
的两个属性
android:shareInterpolator
:表示集合中的动画是否共享同一个插值器,如果集合不指定插值器,那么子动画需要单独指定所需的插值器或者使用默认值。
android:interpolator
:插值器
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:interpolator="@android:anim/accelerate_interpolator"
android:shareInterpolator="true" >
<!-- 透明度起始值,结束值 -->
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0" />
<!-- 平移起始值,结束值 -->
<translate
android:fromXDelta="500"
android:toXDelta="0" />
<!-- 缩放起始值,结束值,缩放轴点x,y坐标 -->
<scale
android:fromXScale="0.5"
android:toXScale="1.2"
android:pivotX="1"
android:pivotY="1" />
<!-- 旋转起始角度,结束角度,旋转轴点x,y坐标 -->
<rotate
android:fromDegrees="0"
android:toDegrees="180"
android:pivotX="1"
android:pivotY="1"/>
</set>
调用方法
Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_animation);
mView.startAnimation(animation);
设置监听
anim.setAnimationListener(new AnimationListener(){
@Override
public void onAnimationEnd(Animation arg0) {}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationStart(Animation animation) {}
});
其他常用属性
// 持续时间
android:duration="100"
// 是否停留在结束位置
android:fillAfter="true"
activity的切换效果要设置在startactivity和finish之后
startActivity(intent);
overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
1.2 布局动画(LayoutAnimation
)属性分析
<layoutAnimation
xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="0.5"
android:animationOrder="reverse"
android:animation="@anim/anim_item"/>
android:delay
:表示子元素开始动画的时间延迟,比如子元素入场动画的时间周期是300ms,那么0.5表示每个子元素都需要延迟150ms才能播放入场动画。
给ViewGroup指定LayoutAnimation的两种方式
//xml
android:layoutAnimation="xxx"
//java
Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_item);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
listView.setLayoutAnimation(controller);
1.3 属性动画
View动画的xml文件放于res/anim/目录下,而属性动画的xml文件则放于res/animator/目录下。
属性动画主要有三个元素:
<animator> <objectAnimator> <set>
相对应的有三个类:ValueAnimator、ObjectAnimator、AnimatorSet。
< animator >
< animator >标签与对应的ValueAnimator类提供了属性动画的核心功能,包括计算动画值、动画时间细节、是否重复等。执行属性动画分两个步骤:
- 计算动画值
- 将动画值应用到对象和属性上
ValueAnimiator只完成第一步,即只计算值,要实现第二步则需要在值变化的监听器里自行更新对象属性。
通过<animator>标签可以很方便的对ValueAnimiator进行设置,可设置的属性如下:
android:duration 动画从开始到结束持续的时长,单位为毫秒
android:startOffset 设置动画执行之前的等待时长,单位为毫秒
android:repeatCount 设置动画重复执行的次数,默认为0,即不重复;可设为-1或infinite,表示无限重复
-
android:repeatMode 设置动画重复执行的模式,可设为以下两个值其中之一:
- restart 动画重复执行时从起点开始,默认为该值
- reverse 动画会反方向执行
android:valueFrom 动画开始的值,可以为int值、float值或color值
android:valueTo 动画结束的值,可以为int值、float值或color值
-
android:valueType 动画值类型,若为color值,则无需设置该属性
- intType 指定动画值,即以上两个value属性的值为整型
- floatType 指定动画值,即以上两个value属性的值为浮点型,默认值
android:interpolator 设置动画速率的变化
在这个例子里,将一个按钮的宽度进行缩放,从100%缩放到20%。
<!-- res/animator/value_animator.xml -->
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:valueFrom="100"
android:valueTo="20"
android:valueType="intType" />
可看到,值的变化从100到20,动画时长3000毫秒,以下则是目标按钮的xml代码:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_btn_normal"
android:onClick="onScaleWidth"
android:text="点我"
android:textColor="@android:color/white" />
按钮默认是填充屏幕宽度的,点击时的执行方法为onScaleWidth,以下则是onScaleWidth方法的代码:
public void onScaleWidth(final View view) {
// 获取屏幕宽度
final int maxWidth = getWindowManager().getDefaultDisplay().getWidth();
ValueAnimator valueAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.value_animator);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
// 当前动画值,即为当前宽度比例值
int currentValue = (Integer) animator.getAnimatedValue();
// 根据比例更改目标view的宽度
view.getLayoutParams().width = maxWidth * currentValue / 100;
view.requestLayout();
}
});
valueAnimator.start();
}
属性动画是通过AnimatorInflater类的loadAnimation()方法获取相应的Animator类实例。
另外,ValueAnimator通过添加AnimatorUpdateListener监听器监听值的变化,从而再手动更新目标对象的属性。
最后,通过调用valueAnimator.start()方法启动动画。
< objectAnimator >
< objectAnimator >标签对应的类为ObjectAnimator,为ValueAnimator的子类。< objectAnimator >标签与< animator >标签不同的是,< objectAnimator >可以直接指定动画的目标对象的属性。标签可设置的属性除了和< animator >一样的那些,另外多了一个:
- android:propertyName 目标对象的属性名,要求目标对象必须提供该属性的setter方法,如果动画的时候没有初始值,还需要提供getter方法
还是用上面的例子,将一个按钮的宽度进行缩放,从100%缩放到20%,但这次改用<objectAnimator>实现。
以下为xml文件的代码:
<!-- res/animator/object_animator.xml -->
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:propertyName="width"
android:valueFrom="100"
android:valueTo="20"
android:valueType="intType" />
与< animator >的例子相比,就只是多了一个android:propertyName的属性,设置值为width。也就是说,动画改变的属性为width,值将从100逐渐减到20。另外,值是从setWidth()传递过去的,再从getWidth()获取。而且,这里设置的值代表的是比例值,因此,还需要进行计算转化为实际的宽度值。最后,对象实际的宽度值为view.getLayoutParams().width。因此,我将用一个包装类来包装原始的view对象,对其提供setWidth()和getWidth()方法,代码如下:
private static class ViewWrapper {
private View target; //目标对象
private int maxWidth; //最长宽度值
public ViewWrapper(View target, int maxWidth) {
this.target = target;
this.maxWidth = maxWidth;
}
public int getWidth() {
return target.getLayoutParams().width;
}
public void setWidth(int widthValue) {
//widthValue的值从100到20变化
target.getLayoutParams().width = maxWidth * widthValue / 100;
target.requestLayout();
}
}
上面setWidth()的代码里,根据比例值转化为了实际的宽度值。最后,动画处理的代码如下:
public void onScaleWidth(View view) {
// 获取屏幕宽度
int maxWidth = getWindowManager().getDefaultDisplay().getWidth();
// 将目标view进行包装
ViewWrapper wrapper = new ViewWrapper(view, maxWidth);
// 将xml转化为ObjectAnimator对象
ObjectAnimator objectAnimator = (ObjectAnimator) AnimatorInflater.loadAnimator(this, R.animator.object_animator);
// 设置动画的目标对象为包装后的view
objectAnimator.setTarget(wrapper);
// 启动动画
objectAnimator.start();
}
ObjectAnimator提供了属性的设置,但相应的需要有该属性的setter和getter方法。而ValueAnimator则只是定义了值的变化,并不指定目标属性,所以也不需要提供setter和getter方法,但只能在AnimatorUpdateListener监听器里手动更新属性。不过,也因为没有指定属性,所以其实更具灵活性了,你可以在监听器里根据值的变化做任何事情,比如更新多个属性,比如在缩放宽度的同时做垂直移动。
为了对View更方便的设置属性动画,Android系统也提供了View的一些属性和相应的setter和getter方法:
- alpha:透明度,默认为1,表示不透明,0表示完全透明
- pivotX 和 pivotY:旋转的轴点和缩放的基准点,默认是View的中心点
- scaleX 和 scaleY:基于pivotX和pivotY的缩放,1表示无缩放,小于1表示收缩,大于1则放大
- rotation、rotationX 和 rotationY:基于轴点(pivotX和pivotY)的旋转,rotation为平面的旋转,rotationX和rotationY为立体的旋转
- translationX 和 translationY:View的屏幕位置坐标变化量,以layout容器的左上角为坐标原点
- x 和 y:View在父容器内的最终位置,是左上角坐标和偏移量(translationX,translationY)的和
< set >
<set>标签对应于AnimatorSet类,可以将多个动画组合成一个动画集,如上面提到的在缩放宽度的同时做垂直移动,可以将一个缩放宽度的动画和一个垂直移动的动画组合在一起。
<set>标签有一个属性可以设置动画的时序关系:
-
android:ordering 设置动画的时序关系,取值可为以下两个值之一:
- together 动画同时执行,默认值
-
sequentially 动画按顺序执行
那如果想有些动画同时执行,有些按顺序执行,该怎么办呢?因为<set>标签是可以嵌套其他<set>标签的,也就是说可以将同时执行的组合在一个<set>标签,再嵌在按顺序执行的<set>标签内。
示例代码:
<!-- res/animator/animator_set.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together">
<objectAnimator
android:duration="3000"
android:propertyName="width"
android:valueFrom="100"
android:valueTo="20"
android:valueType="intType" />
<objectAnimator
android:duration="3000"
android:propertyName="marginTop"
android:valueFrom="0"
android:valueTo="100"
android:valueType="intType" />
</set>
以上代码可实现两个同时执行的动画,一个将width从100缩放到20,一个将marginTop从0增加到100。多了一个marginTop属性,那么,在ViewWrapper添加setMarginTop()方法,添加后的ViewWrapper类代码如下:
private static class ViewWrapper {
private View target;
private int maxWidth;
public ViewWrapper(View target, int maxWidth) {
this.target = target;
this.maxWidth = maxWidth;
}
public int getWidth() {
return target.getLayoutParams().width;
}
public void setWidth(int widthValue) {
target.getLayoutParams().width = maxWidth * widthValue / 100;
target.requestLayout();
}
public void setMarginTop(int margin) {
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) target.getLayoutParams();
layoutParams.setMargins(0, margin, 0, 0);
target.setLayoutParams(layoutParams);
}
}
最后,动画处理的代码:
public void onScaleWidth(View view) {
// 获取屏幕宽度
int maxWidth = getWindowManager().getDefaultDisplay().getWidth();
// 将目标view进行包装
ViewWrapper wrapper = new ViewWrapper(view, maxWidth);
// 将xml转化为ObjectAnimator对象
AnimatorSet animatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.animator_set);
// 设置动画的目标对象为包装后的view
animatorSet.setTarget(wrapper);
// 启动动画
animatorSet.start();
}
1.4 插值器和估值器:属性动画实现非匀速动画的重要手段
时间插值器(TimeInterpolator
)的作用是根据时间流逝的百分比计算出当前属性值改变的百分比,系统内置的插值器有线性插值器(LinearInterpolator
)、加速减速插值器(AccelerateDecelerateInterpolator
)和减速插值器(DecelerateInterpolator
)。
类型估值器(TypeEvaluator
)的作用是根据当前属性改变的百分比计算出改变后的属性值,系统内置的估值器有IntEvaluator
、FloatEvaluator
和ArgbEvaluator
(针对颜色)。
1.5 使用动画的注意事项
(1)OOM:尽量避免使用帧动画,使用的话应尽量避免使用过多尺寸较大的图片;
(2)内存泄露:属性动画中的无限循环动画需要在Activity退出的时候及时停止(clearAnimation),否则将导致Activity无法释放而造成内存泄露。view动画不存在这个问题;
(3)兼容性问题:某些动画在3.0以下系统上有兼容性问题;
(4)view动画的问题:view动画是对view的影像做动画,并不是真正的改变view的状态,因此有时候动画完成之后view无法隐藏,即setVisibility(View.GONE)
失效了,此时需要调用view.clearAnimation()
清除view动画才行。
(5)不要使用px;
(6)动画元素的交互:在android3.0以前的系统上,view动画和属性动画,新位置均无法触发点击事件,同时,老位置仍然可以触发单击事件。从3.0开始,属性动画的单击事件触发位置为移动后的位置,view动画仍然在原位置;
(7)硬件加速:使用动画的过程中,建议开启硬件加速,这样会提高动画的流畅性。
参考:
http://keeganlee.me/post/android/20151026
《android 开发艺术探索》——任玉刚