Android动画之View动画详解

在Android开发中,为了提高用户的交互体验,经常会使用动画来模拟物理世界中人们对事物的操作。Android系统提供了View动画和属性动画两种方式,开发者可根据需求选择不同的动画方式。这篇文章我们先详细了解一下View动画的使用。

View动画,正如其命名,其作用对象是View。它支持4中动画方式,分别是平移,旋转,缩放和渐变。对应的类分别为TranslateAnimation,
RotateAnimation, ScaleAnimation和AlphaAnimation。这四种动画方式都继承自Animation,在使用时我们需要设置相应的属性值,如动画执行时间,动画重复次数,重复模式等,下面是使用View动画时的伪代码:

1
2
3
4
5
6
7
8
9
10
// 定义动画方式
Animation animation = new ChildAnimation(...);
// 设置动画的执行时间
animation.setDuration(400);
// 设置动画的重复次数,-1表示无限重复,0表示不重复
animation.setRepeatCount(-1);
// 设置动画重复时的切换模式(RESTART表示重新执行,REVERSE表示动画反操作)
animation.setRepeatMode(Animation.REVERSE);
// 开始执行动画
view.startAnimation(animation);

下面我们具体介绍一下这四个ChildAnimation:

平移:根据设置的初始点和终点的坐标,沿直线移动。

1
2
3
4
5
6
7
8
9
10
/**
* @param fromXDelta: View移动的起点的X坐标
*
* @param toXDelta: View移动的终点的X坐标
*
* @param fromYDelta: View移动的起点的Y坐标
*
* @param toYDelta: View移动的终点的Y坐标
*/

public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta);

旋转:让View根据设定值旋转一定角度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @param fromDegrees: View旋转的起点角度
*
* @param toDegrees: View旋转的终点角度
*
* 注意: 这种构造方式构造的旋转动画,旋转时以View左上角为中心进行旋转
*/

public RotateAnimation(float fromDegrees, float toDegrees)

/**
* @param fromDegrees: View旋转的起点角度
*
* @param toDegrees: View旋转的终点角度
*
* @param pivotX: 旋转中心的X坐标
*
* @param pivotY: 旋转中心的Y坐标
*/

public RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY)

缩放:让View根据设定值进行缩放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* @param fromX: 动画起始时X坐标的缩放因子
*
* @param toX: 动画结束时X的缩放因子
*
* @param fromY: 动画起始时Y坐标的缩放因子
*
* @param toY: 动画结束时Y的缩放因子
*
* 注意: 参数等于1表示View原来的尺寸,from>to表示表示缩小,from<to表示放大;默认缩放中心为View的左上角。
*/

public ScaleAnimation(float fromX, float toX, float fromY, float toY)

/**
* @param fromX: 动画起始时X坐标的缩放因子
*
* @param toX: 动画结束时X的缩放因子
*
* @param fromY: 动画起始时Y坐标的缩放因子
*
* @param toY: 动画结束时Y的缩放因子
*
* @param pivotX: 旋转中心的X坐标
*
* @param pivotY: 旋转中心的Y坐标
*/

public ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY)

渐变动画:让View实现渐变效果

1
2
3
4
5
6
/**
* @param fromAlpha: 渐变开始时View的Alpha值(1表示View完全不透明,0表示View完全透明)
*
* @param toAlpha: 渐变结束时View的Alpha值
*/

public AlphaAnimation(float fromAlpha, float toAlpha);

示例代码

java代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* 平移动画示例代码
* @param view
*/

public void translateAnimation(View view) {
TranslateAnimation animation = new TranslateAnimation(view.getLeft(), view.getLeft()+200, 0, 0);
animation.setDuration(500);
animation.setRepeatCount(0);
animation.setFillAfter(true);
view.startAnimation(animation);
}

/**
* 旋转动画示例代码
* @param view
*/

public void rotateAnimation(View view) {
RotateAnimation animation = new RotateAnimation(0, 360, view.getWidth()/2, view.getHeight()/2);
animation.setDuration(1000);
animation.setRepeatCount(-1);
animation.setRepeatMode(Animation.RELATIVE_TO_SELF);
view.startAnimation(animation);
}

/**
* 缩放动画示例代码
* @param view
*/

public void scaleAnimation(View view) {
ScaleAnimation animation = new ScaleAnimation(1.0f, 1.2f,
1.0f, 1.2f, view.getWidth()/2, view.getHeight()/2);
animation.setDuration(1000);
animation.setRepeatCount(-1);
animation.setRepeatMode(Animation.RELATIVE_TO_SELF);
view.startAnimation(animation);
}

/**
* 渐变动画示例代码
* @param view
*/

public void alphaAnimation(View view) {
AlphaAnimation animation = new AlphaAnimation(1.0f, 0.5f);
animation.setDuration(400);
animation.setRepeatCount(-1);
animation.setRepeatMode(Animation.REVERSE);
view.startAnimation(animation);
}

xml方式实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!-- 平移动画的xml实现方式 -->
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="100"
android:toYDelta="0"
android:duration="500">

</translate>

<!-- 旋转动画的xml实现方式 -->
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="0.5"
android:pivotY="0.5"
android:duration="500">

</rotate>

<!-- 缩放动画的xml实现方式 -->
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0.5"
android:toYScale="0.5"
android:pivotX="0.5"
android:pivotY="0.5"
android:duration="500">

</scale>

<!-- 渐变动画的xml实现方式 -->
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="1.0"
android:toAlpha="0.5"
android:duration="500">

</alpha>

使用方式:

  1. 在res目录下新建一个资源文件夹anim, 在其中定义需要的动画资源,例如命名为animation.xml;
  2. 在代码中通过AnimationUtils工具类加载该动画资源文件;
  3. 开始执行动画。
1
2
Animation  animation = AnimationUtils.loadAnimation(this, R.anim.animation);
view.startAnimation(animation);

经典使用场景

为ViewGroup添加动画

既然View动画作用于View,ViewGroup作为View的子类,自然也可以使用View动画,用法和普通的View一样,其表现形式就是整个ViewGroup随预设的动画进行变化。这里我们介绍ViewGroup另一种更炫酷的动画方式--LayoutAnimation,其特点是ViewGroup中的子View可以根据设定进行延迟加载。这中方式常用于ListView中,下面就以ListView为例讲解一下这种方式的使用过程:

xml的实现方式:

先定义LayoutAnimation—-anim_layout.xml:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
<!--子元素的延迟加载时间,比如动画的执行时间是200ms, 0.5就表示延迟100ms,即第一个元素动画执行100ms后第二个元素开始执行动画-->
android:delay="0.5"
<!-- 子元素的动画资源 -->
android:animation="@anim/splash_push_right_in"
<!-- 子元素的动画顺序 normal表示依次执行,reverse表示逆序执行,random表示随机执行 -->
android:animationOrder="normal">
</layoutAnimation>

接下来为ViewGroup设置layoutAnimation属性即可。

1
2
3
4
5
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutAnimation="@anim/anim_layout"/>

代码的实现方式

1
2
3
4
5
Animation animation = AnimationUtils.loadAnimation(getActivity(), R.anim.splash_push_right_in);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
listView.setLayoutAnimation(controller);

无论采用哪种方式,都可以实现ViewGroup中item动画延迟加载的效果。但是对于ListView, 只能看到只有初次加载时可见区域中Item的动画延迟加载,翻页时动画已消失,所以这种方式用在ListView中还是有很大的局限性。

为Activity添加切换动画

一般来说,对于一款App,我们需要统一的页面切换方式。对此我们可以为Activity设置统一的切换动画。Activity提供了设置切换动画的方法:

1
2
3
4
5
6
7
8
/**
* @param enterAnim: 进入页面时的动画资源ID
*
* @param exitAnim: 退出页面时的动画资源ID
*
* 注意:此函数只有使用在startActivity()或finish()之后才起作用。
*/
public void overridePendingTransition(int enterAnim, int exitAnim);

示例代码:

1
2
3
4
5
6
7
8
// 进入时的动画:
Intent intent = new Intent(this, OtherActivity.class);
intent.startActivity(intent);
overridePendingTransition(R.anim.right_in_anim, R.anim.right_out_anim);

// 退出时的动画:
finish();
overridePendingTransition(R.anim.right_in_anim, R.anim.right_out_anim);

每次在进入Activity或退出Activity时都设置动画感觉很繁琐,我们可以将动画设置放在BaseActivity中进行,这样继承自BaseActivity的所有Activity都有了统一的切换效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract class BaseActivity extends FragmentActivity {
protected void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
// 设置进入Activity时的动画(既然overridePendingTransition放在startActivity()之后才会生效,
// 那么我们将其放在需要跳转的Activity生命周期的开始也是满足条件的)
overridePendingTransition(R.anim.right_in_anim, R.anim.right_out_anim);
setContentView(...);
...
}

...

@Override
public void finish() {
super.finish();
// 设置退出时的动画(因为overridePendingTransition在finish()之后才起作用,所以重写时必须写在super.finish()之后)
overridePendingTransition(R.anim.right_in_anim, R.anim.right_out_anim);
}

注意点

  • View动画并不会真正改变View的状态,所以在使用时会发现View虽然变了,但终归会恢复原样的,即便repeatCount设置为0。如果想让View最终显示执行动画后的结果,只需要设置fillAfter为true即可;
1
2
// 让View执行动画后停留在终点位置,否则View会恢复到原来的状态
animation.setFillAfter(true);
  • View动画执行后,会发现View的点击事件失效,但是View移动前的位置却依然能够响应View的点击事件。这是View动画本身存在的缺陷,这种致命的缺陷也限制了它的使用范围:只能用于处理没有事件的View。
  • 在View动画使用过程中不要使用px, 以免出现屏幕分辨率对动画的影响;
  • 动画执行结束后View不能隐藏。即setVisibility(View.GONE)失效,此时只需要调用clearAnimation()清除View的动画即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 动画的监听过程
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {

}

@Override
public void onAnimationEnd(Animation animation) {
// 清除View动画
view.clearAnimation();
view.setVisibility(View.GONE);
}

@Override
public void onAnimationRepeat(Animation animation) {

}
});

参考书籍

《Android开发艺术探索》—— 任玉刚

,