一.前言

1.三种动画的关系

2.三种动画的继承关系

二.逐帧动画(Frame Animation)

简单讲就是把几个静态的图片(放在res/drawable/路径下)快速播放形成动画,实现的方式有 Xml方式 或者 Java代码,官方推荐使用 Xml 方式。

效果展示:

1. 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
<!-- fire_frame_animation.xml -->
<?xml version="1.0" encoding="utf-8"?>
<!--
oneshot:动画执行次数
true: 执行一次
false:执行无数次
-->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<!--
item:每个item对应一帧
drawable:配置当前这一帧显示的图片
duration:配置当前这一帧显示的时长 单位 ms
-->
<item android:drawable="@drawable/campfire01" android:duration="200"/>
<item android:drawable="@drawable/campfire02" android:duration="200"/>
<item android:drawable="@drawable/campfire03" android:duration="200"/>
<item android:drawable="@drawable/campfire04" android:duration="200"/>
<item android:drawable="@drawable/campfire05" android:duration="200"/>
<item android:drawable="@drawable/campfire06" android:duration="200"/>
<item android:drawable="@drawable/campfire07" android:duration="200"/>
<item android:drawable="@drawable/campfire08" android:duration="200"/>
<item android:drawable="@drawable/campfire09" android:duration="200"/>
<item android:drawable="@drawable/campfire10" android:duration="200"/>
<item android:drawable="@drawable/campfire11" android:duration="200"/>
<item android:drawable="@drawable/campfire12" android:duration="200"/>
<item android:drawable="@drawable/campfire13" android:duration="200"/>
<item android:drawable="@drawable/campfire14" android:duration="200"/>
<item android:drawable="@drawable/campfire15" android:duration="200"/>
<item android:drawable="@drawable/campfire16" android:duration="200"/>
<item android:drawable="@drawable/campfire17" android:duration="200"/>
</animation-list>
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
//MainActivity
public class MainActivity extends AppCompatActivity {

//全局化
AnimationDrawable animationDrawable;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//获得控件
ImageView imageView = findViewById(R.id.frame_image_view);

//获得AnimationDrawable
animationDrawable = (AnimationDrawable) imageView.getDrawable();
}

/**
* 开启逐帧动画
* @param view
*/
public void start_frame_animation(View view) {
animationDrawable.start();
}

/**
* 停止逐帧动画
* @param view
*/
public void stop_frame_animation(View view) {
animationDrawable.stop();
}
}
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
<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">

<ImageView
android:id="@+id/frame_image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/fire_frame_animation"
android:scaleType="fitXY"
/>

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="start"
android:layout_alignParentStart="true"
android:layout_marginStart="20dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="20dp"
android:onClick="start_frame_animation"
/>

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="stop"
android:layout_alignParentEnd="true"
android:layout_marginEnd="20dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="20dp"
android:onClick="stop_frame_animation"
/>

</RelativeLayout>

2.Java代码

  • Xml方式使用的 item,java代码使用public void addFrame(@NonNull Drawable frame, int duration)方法达到相同的效果。
  • Xml方式直接在Xml布局文件里面给ImageView设置动画Xml文件(fire_frame_animation.xml)作为背景,java代码使用public void setImageDrawable(@Nullable Drawable drawable)方法设置AnimationDrawable的对象为背景。
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
49
50
//MainActivity
public class MainActivity extends AppCompatActivity {

//全局化
AnimationDrawable animationDrawable;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//创建AnimationDrawable
animationDrawable = new AnimationDrawable();
//animationDrawable.setOneShot(true);

//添加每一帧的动画
int[] resID = {R.drawable.campfire01,R.drawable.campfire02,
R.drawable.campfire03, R.drawable.campfire04,
R.drawable.campfire05,R.drawable.campfire06,
R.drawable.campfire07,R.drawable.campfire08,
R.drawable.campfire09,R.drawable.campfire10,
R.drawable.campfire11,R.drawable.campfire12,
R.drawable.campfire13,R.drawable.campfire14,
R.drawable.campfire15,R.drawable.campfire16,
R.drawable.campfire17};
for (int i = 0; i < resID.length; i++) {
animationDrawable.addFrame(getResources().getDrawable(resID[i],null),100);
}

//找到控件
ImageView imageView = findViewById(R.id.frame_image_view);
imageView.setImageDrawable(animationDrawable);
}

/**
* 开启逐帧动画
* @param view
*/
public void start_frame_animation(View view) {
animationDrawable.start();
}

/**
* 停止逐帧动画
* @param view
*/
public void stop_frame_animation(View view) {
animationDrawable.stop();
}
}
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
<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<ImageView
android:id="@+id/frame_image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
/>

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="start"
android:layout_alignParentStart="true"
android:layout_marginStart="20dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="20dp"
android:onClick="start_frame_animation"
/>

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="stop"
android:layout_alignParentEnd="true"
android:layout_marginEnd="20dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="20dp"
android:onClick="stop_frame_animation"
/>
</RelativeLayout>

三.补间动画(Tween Animation)

补间动画就是指开发者指定动画的开始、动画的结束的”关键帧”,而动画变化的”中间帧”由系统计算,并补齐。

补间动画有四种:

  • 位移 TranslateAnimation

  • 缩放 ScaleAnimation

  • 旋转 RotateAnimation

  • 透明度 AlphaAnimation

1. Xml方式

  • 系统默认不会在 res 文件夹下面创建 anim 文件夹,我们需要手动创建 anim 文件夹。我们的动画xml文件就存放在这里面。
  • 我们使用的AnimationUtilspublic static Animation loadAnimation(Context context, @AnimRes int id)方法加载我们的动画xml文件。
  • 四种方式共有的属性:
1
2
3
4
5
6
7
8
9
10
android:duration="3000"    // 动画持续时间(ms),必须设置,动画才有效果
android:startOffset="1000" // 动画延迟开始时间(ms)
android:fillBefore="true" // 用于确定动画开始时,View的动画属性值,默认为true
// fillBefore为true,在startOffset阶段时,将动画属性设置为给与的初始值
// fillBefore为false,将动画属性设置为View本身的初始值
android:fillAfter="false" // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
android:fillEnabled="true" // 是否应用fillBefore值,对fillAfter值无影响,默认为false
android:repeatMode="restart" // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart
android:repeatCount="0" // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度

① 位移 TranslateAnimation

Translate独有的属性:

1
2
3
4
android:fromXDelta="0"   //视图在水平方向x移动的起始值
android:toXDelta="100" //视图在水平方向x移动结束始值
android:fromYDelta="0" //视图在垂直方向y移动的起始值
android:toYDelta="100" //视图在垂直方向y移动的结束值

运行效果的代码:

1
2
3
4
5
6
7
8
9
10
11
12
//动画 translate_animation.xml 文件
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:toXDelta="100"
android:fromYDelta="0"
android:toYDelta="100"

android:duration="1000"
android:repeatCount="1"
android:repeatMode="restart"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Activity 界面
public class TranslateActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_translate);

//找到需要动画的控件
final View view = findViewById(R.id.translate_view);

//加载Xml东动画文件
final Animation translate = AnimationUtils.loadAnimation(this, R.anim.translate_animation);

//开始动画
findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
view.startAnimation(translate);
}
});
}
}

运行效果:

② 缩放 ScaleAnimation

Scale独有的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
android:fromXScale="1"     //动画在水平方向x的起始缩放倍数
android:toXScale="1.5" //动画在水平方向x的结束缩放倍数
android:fromYScale="1" //动画在竖直方向y的起始缩放倍数
android:toYScale="1.5" //动画在竖直方向y的结束缩放倍数

//缩放倍数 0.0 表示收缩到没有,1.0 表示正常无收缩
//缩放倍数 < 1.0,收缩
//缩放倍数 > 1.0 放大

android:pivotX="100%" //缩放轴点的x坐标
android:pivotY="100%" //缩放轴点的y坐标

//缩放轴点取值类型:数值,百分比,百分比p
//数值(如50):轴点为View的左上角的原点在x方向或y方向加上50px的点。
//百分比(如50%):轴点为View的左上角的原点在x方向加上自身宽度50%或y方向加上自身高度50%的点
//百分比p(如50%p):轴点为View的左上角的原点在x方向加上父控件宽度50%或y方向加上父控件高度50%的点。

运行效果的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//动画 scale_animation.xml 文件
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXScale="1"
android:toXScale="1.5"
android:fromYScale="1"
android:toYScale="1.5"
android:pivotX="100%"
android:pivotY="100%"

android:duration="1000"
android:repeatCount="1"
android:repeatMode="restart"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Activity 界面
public class ScaleActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scale);

//找到需要动画的控件
final View view = findViewById(R.id.translate_view);

//加载Xml东动画文件
final Animation scale = AnimationUtils.loadAnimation(this, R.anim.scale_animation);

//开始动画
findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
view.startAnimation(scale);
}
});
}
}

运行效果:

③ 旋转 RotateAnimation

Rotate独有的属性:

1
2
3
4
5
6
7
8
9
10
android:fromDegrees="0"    // 动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
android:toDegrees="270" // 动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)

android:pivotX="100%" //缩放轴点的x坐标
android:pivotY="100%" //缩放轴点的y坐标

//缩放轴点取值类型:数值,百分比,百分比p
//数值(如50):轴点为View的左上角的原点在x方向或y方向加上50px的点。
//百分比(如50%):轴点为View的左上角的原点在x方向加上自身宽度50%或y方向加上自身高度50%的点
//百分比p(如50%p):轴点为View的左上角的原点在x方向加上父控件宽度50%或y方向加上父控件高度50%的点。

运行效果的代码:

1
2
3
4
5
6
7
8
9
10
11
12
//动画 rotate_animation.xml 文件
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="90"
android:pivotX="100%"
android:pivotY="100%"

android:duration="1000"
android:repeatCount="1"
android:repeatMode="restart"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class RotateActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rotate);

//找到需要动画的控件
final View view = findViewById(R.id.translate_view);

//加载Xml东动画文件
final Animation rotate = AnimationUtils.loadAnimation(this, R.anim.rotate_animation);

//开始动画
findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
view.startAnimation(rotate);
}
});
}
}

运行效果:

④ 透明度 AlphaAnimation

Alpha独有的属性:

1
2
android:fromAlpha="1.0" // 动画开始时视图的透明度(取值范围: 0.0 ~ 1.0)
android:toAlpha="0.0" // 动画结束时视图的透明度(取值范围: 0.0 ~ 1.0)

运行效果的代码:

1
2
3
4
5
6
7
8
9
10
//动画 alpha_animation.xml 文件
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="1"
android:toAlpha="0.5"

android:duration="1000"
android:repeatCount="1"
android:repeatMode="restart"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Activity 界面
public class AlphaActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_alpha);

//找到需要动画的控件
final View view = findViewById(R.id.translate_view);

//加载Xml东动画文件
final Animation alpha = AnimationUtils.loadAnimation(this, R.anim.alpha_animation);

//开始动画
findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
view.startAnimation(alpha);
}
});
}
}

运行效果:

⑤ 组合

  • 同时使用平移、缩放、旋转 & 透明度4种动画,即组合动画

  • 使用组合动画需要用到标签 < Set/>

  • 组合动画播放时是全部动画同时开始,如果想不同动画不同时间开始就要使用android:startOffset属性来延迟单个动画播放时间。

  • 组合动画独有的属性:

    1
    2
    3
    android:shareinterpolator = “true
    // 表示组合动画中的动画是否和集合共享同一个差值器
    // 如果集合不指定插值器,那么子动画需要单独设置
  • 组合动画同样具备哪些共有的属性:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    android:duration="3000"      // 动画持续时间(ms),必须设置,动画才有效果
    android:startOffset="1000" // 动画延迟开始时间(ms)
    android:fillBefore="true" // 用于确定动画开始时,View的动画属性值,默认为true
    // fillBefore为true,在startOffset阶段时,将动画属性设置为给与的初始值
    // fillBefore为false,将动画属性设置为View本身的初始值
    android:fillAfter="false" // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
    android:fillEnabled="true" // 是否应用fillBefore值,对fillAfter值无影响,默认为false
    android:repeatMode="restart" // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart
    android:repeatCount="0" // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
    android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度

关于android:fillBeforeandroid:fillEnabled的使用。首先说结论,如果fillEnabled为false,那么不管fillBefore取何值,都是fillBefore为true的效果。

1.两个效果的实现:

  • View先在原位置从0.5倍放大到2倍,然后在从原位置的45度旋转到720度。效果图和代码如下所示:

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
<!-- set_animation.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillBefore="false"
android:fillAfter="false"
>

<scale
android:fromXScale="0.5"
android:toXScale="2.0"
android:fromYScale="0.5"
android:toYScale="2.0"
android:pivotX="50%"
android:pivotY="50%"

android:duration="3000"
android:fillEnabled="false"
/>

<rotate
android:fromDegrees="45"
android:toDegrees="720"
android:pivotX="50%"
android:pivotY="50%"

android:duration="2000"
android:startOffset="3000"
android:fillEnabled="true"
/>
</set>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Activity
public class SetActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_set);

//找到需要动画的控件
final View view = findViewById(R.id.translate_view);

//加载Xml东动画文件
final Animation scale = AnimationUtils.loadAnimation(this, R.anim.set_animation);

//开始动画
findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
view.startAnimation(scale);
}
});
}
}
  • 初始状态为View偏90度,大小为View本身大小的一半,在接下来的1秒内变为View本身大小的2倍,然后,再在接下来的2秒内旋转到720度的位置,最后变为View本身的大小。运行效果如下:(其实就是第一个效果中的Xml代码去掉全部的android:fillEnabled=”true”和android:fillBefore=”false”,其他不变,就会达成我们当前所描述的效果)

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
<!-- set_animation.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="false"
>

<scale
android:fromXScale="0.5"
android:toXScale="2.0"
android:fromYScale="0.5"
android:toYScale="2.0"
android:pivotX="50%"
android:pivotY="50%"

android:duration="3000"
/>

<rotate
android:fromDegrees="45"
android:toDegrees="720"
android:pivotX="50%"
android:pivotY="50%"

android:duration="2000"
android:startOffset="3000"
/>
</set>

2.两种运行效果的解释:

  • 除去全部的android:fillEnabled=”true”和android:fillBefore=”false”之后,fillEnabled默认为false,fillBefore默认为true。而fillEnabled为false、fillBefore为any相当于fillEnabled为true且fillBefore为true,也就是动画开始时,动画属性要设置成给与的样式,View偏90度,大小为View本身大小的一半

  • 除去全部的android:fillEnabled=”true”和android:fillBefore=”false”之前,我们一开始设置动画集合的fillBefore为false,缩放动画的fillEnabled为false,也就是说初始的缩放状态是大小为View本身大小的一半。旋转动画的fillEnabled为true,动画集合的fillBefore为false,也就是说初始的旋转状态是View本身的初始值。结合起来就是View不偏转,大小为View本身大小的一半

2.Java代码

java代码主要是使用继承于 Animation 的 TranslateAnimation,ScaleAnimation,RotateAnimation,AlphaAnimation,AnimationSet。和 Xml 共有的属性一样,这些类也共有对应的方法(来自父类 Animation):

1
2
3
4
5
6
7
8
9
10
public void setDuration(long durationMillis)    // 动画持续时间(ms),必须设置,动画才有效果
public void setStartOffset(long startOffset) // 动画延迟开始时间(ms)
public void setFillBefore(boolean fillBefore) // 用于确定动画开始时,View的动画属性值,默认为true
// fillBefore为true,在startOffset阶段时,将动画属性设置为给与的初始值
// fillBefore为false,将动画属性设置为View本身的初始值
public void setFillAfter(boolean fillAfter) // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
public void setFillEnabled(boolean fillEnabled) // 是否应用fillBefore值,对fillAfter值无影响,默认为false
public void setRepeatMode(int repeatMode) // 选择重复播放动画模式,Animation.RESTART 代表正序重放,Animation.REVERSE 代表倒序回放,默认为Animation.RESTART
public void setRepeatCount(int repeatCount) // 重放次数(所以动画的播放次数=重放次数+1),为Animation.INFINITE 时无限重复
public void setInterpolator(Interpolator i) // 插值器,即影响动画的播放速度

① 位移 TranslateAnimation

1
2
3
4
5
//fromXDelta:视图在水平方向x移动的起始值
//toXDelta:视图在水平方向x移动结束始值
//fromYDelta:视图在垂直方向y移动的起始值
//toYDelta:视图在垂直方向y移动的结束值
public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)

运行效果的代码:

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
public class TranslateActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_translate);

//找到需要动画的控件
final View view = findViewById(R.id.translate_view);

//创建 TranslateAnimation 动画类
final TranslateAnimation translate = new TranslateAnimation(0,100,0,100);

//设置动画时间
translate.setDuration(1000);

//设置重复次数 重复次数 0 代表 执行一次
translate.setRepeatCount(1);

//设置重复模式
translate.setRepeatMode(Animation.RESTART);

//开始动画
findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
view.startAnimation(translate);
}
});
}
}

运行效果:

② 缩放 ScaleAnimation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//fromX:动画在水平方向x的起始缩放倍数
//toX:动画在水平方向x的结束缩放倍数
//fromY:动画在竖直方向y的起始缩放倍数
//toY:动画在竖直方向y的结束缩放倍数
//pivotXType:缩放轴点的x坐标的模式
//pivotXValue:缩放轴点x坐标的相对值
//pivotYType:缩放轴点的y坐标的模式
//pivotYValue:缩放轴点y坐标的相对值
public ScaleAnimation(float fromX, float toX, float fromY, float toY,
int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)

//缩放倍数 0.0f 表示收缩到没有,1.0f 表示正常无收缩
//缩放倍数 < 1.0f,收缩
//缩放倍数 > 1.0f 放大

//缩放轴点取值类型:
//Animation.ABSOLUTE: View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
//Animation.RELATIVE_TO_SELF: View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
//Animation.RELATIVE_TO_PARENT: View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)

运行效果的代码:

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
public class ScaleActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scale);

//找到需要动画的控件
final View view = findViewById(R.id.translate_view);

//创建 ScaleAnimation 动画类
final ScaleAnimation scale = new ScaleAnimation(1.0f,1.5f,1.0f,1.5f,Animation.RELATIVE_TO_SELF,1.0f,Animation.RELATIVE_TO_SELF,1.0f);

//设置动画时间
scale.setDuration(1000);

//设置重复次数 重复次数 0 代表 执行一次
scale.setRepeatCount(1);

//设置重复模式
scale.setRepeatMode(Animation.RESTART);

//开始动画
findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
view.startAnimation(scale);
}
});
}
}

运行效果:

③ 旋转 RotateAnimation

1
2
3
4
5
6
7
8
9
10
11
12
13
//fromDegrees:动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
//toDegrees:动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
//pivotXType:缩放轴点的x坐标的模式
//pivotXValue:缩放轴点x坐标的相对值
//pivotYType:缩放轴点的y坐标的模式
//pivotYValue:缩放轴点y坐标的相对值
public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
int pivotYType, float pivotYValue)

//缩放轴点取值类型:
//Animation.ABSOLUTE: View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
//Animation.RELATIVE_TO_SELF: View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
//Animation.RELATIVE_TO_PARENT: View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)

运行效果的代码:

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
public class RotateActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rotate);

//找到需要动画的控件
final View view = findViewById(R.id.translate_view);

//创建 RotateAnimation 动画类
final RotateAnimation rotate = new RotateAnimation(0f,90f,Animation.RELATIVE_TO_SELF,1.0f,Animation.RELATIVE_TO_SELF,1.0f);

//设置动画时间
rotate.setDuration(1000);

//设置重复次数 重复次数 0 代表 执行一次
rotate.setRepeatCount(1);

//设置重复模式
rotate.setRepeatMode(Animation.RESTART);

//开始动画
findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
view.startAnimation(rotate);
}
});

}
}

运行效果:

④ 透明度 AlphaAnimation

1
2
3
//fromAlpha:动画开始时视图的透明度(取值范围: 0.0f ~ 1.0f)
//toAlpha:动画结束时视图的透明度(取值范围: 0.0f ~ 1.0f)
public AlphaAnimation(float fromAlpha, float toAlpha)

运行效果的代码:

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
public class AlphaActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_alpha);

//找到需要动画的控件
final View view = findViewById(R.id.translate_view);

//创建 AlphaAnimation 动画类
final AlphaAnimation alpha = new AlphaAnimation(1.0f,0.5f);

//设置动画时间
alpha.setDuration(1000);

//设置重复次数 重复次数 0 代表 执行一次
alpha.setRepeatCount(1);

//设置重复模式
alpha.setRepeatMode(Animation.RESTART);

//开始动画
findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
view.startAnimation(alpha);
}
});

}
}

运行效果:

⑤ 组合动画

1
2
3
AnimationSet                                        //类似于set标签,动画集合类
public AnimationSet(boolean shareInterpolator) //AnimationSet的构造方法,shareInterpolator控制组合动画中的动画是否和集合共享同一个差值器
public void addAnimation(Animation a) //将子动画加入到组合动画中

运行效果代码以及例子:

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
public class SetActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_set);

//找到需要动画的控件
final View view = findViewById(R.id.translate_view);

//创建 组合动画类
final AnimationSet animationSet = new AnimationSet(true);

//设置动画集合的相关属性
animationSet.setFillBefore(false);
animationSet.setFillAfter(false);

//缩放动画
ScaleAnimation scaleAnimation = new ScaleAnimation(0.5f, 2.0f, 0.5f, 2.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(3000);
scaleAnimation.setFillEnabled(false);

//旋转动画
RotateAnimation rotateAnimation = new RotateAnimation(45, 720, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(2000);
rotateAnimation.setFillEnabled(true);
rotateAnimation.setStartOffset(3000);

//将子动画添加到动画集合中
animationSet.addAnimation(scaleAnimation);
animationSet.addAnimation(rotateAnimation);

//开始动画
findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
view.startAnimation(animationSet);
}
});
}
}

3.高级使用

① 监听动画

  • Animation类通过监听动画开始 / 结束 / 重复时刻来进行一系列操作,如跳转页面等等。
  • 通过在 Java 代码里setAnimationListener()方法设置。
  • 若采取上述方法监听动画,每次监听都必须重写4个方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
// 动画开始时回调
}

@Override
public void onAnimationEnd(Animation animation) {
// 动画结束时回调
}

@Override
public void onAnimationRepeat(Animation animation) {
//动画重复执行的时候回调
}
});
  • 采用动画适配器AnimatorListenerAdapter,解决实现接口繁琐的问题。
1
2
3
4
5
6
7
8
9
anim.addListener(new AnimatorListenerAdapter() {  
// 向addListener()方法中传入适配器对象AnimatorListenerAdapter()
// 由于AnimatorListenerAdapter中已经实现好每个接口
// 所以这里不实现全部方法也不会报错
@Override
public void onAnimationStart(Animator animation) {
// 如想只想监听动画开始时刻,就只需要单独重写该方法就可以
}
});

② Interpolator(插值器)

Android动画-插值器(Interpolator)

4.Activity 的切换效果实战

① 系统预设

系统已经封装好了淡入淡出、左滑右滑的效果,具体使用如下:

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
/**
* 启动动画
*/
Intent intent = new Intent (this,Acvtivity.class);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
// 采用overridePendingTransition(int enterAnim, int exitAnim)进行设置
// enterAnim:从Activity a跳转到Activity b,进入b时的动画效果资源ID
// exitAnim:从Activity a跳转到Activity b,离开a时的动画效果资源Id

// 特别注意
// overridePendingTransition()必须要在startActivity(intent)后被调用才能生效

/**
* 结束动画
*/
@Override
public void finish(){
super.finish();
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);

// 采用overridePendingTransition(int enterAnim, int exitAnim)进行设置
// enterAnim:从Activity a跳转到Activity b,进入b时的动画效果资源ID
// exitAnim:从Activity a跳转到Activity b,离开a时的动画效果资源Id

// 特别注意
// overridePendingTransition()必须要在finish()后被调用才能生效
}

实际例子:

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
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

final Intent intent = new Intent(this, SecondActivity.class);

findViewById(R.id.fade_animation).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(intent);

//淡入淡出
overridePendingTransition(android.R.anim.fade_in,android.R.anim.fade_out);
}
});

findViewById(R.id.slide_animation).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(intent);

//从左向右滑动
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right);
}
});
}
}

② 自定义切换效果

1> 淡入淡出 效果是采用透明度动画(Alpha)。

fade_in.xml(淡入)

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>  
<set xmlns:android="http://schemas.android.com/apk/res/android" >

<alpha
android:duration="1500"
android:fromAlpha="0.0"
android:toAlpha="1.0" />

</set>

fade_out.xml(淡出)

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>  
<set xmlns:android="http://schemas.android.com/apk/res/android" >

<alpha
android:duration="1500"
android:fromAlpha="1.0"
android:toAlpha="0.0" />

</set>

在Java代码中设置

1
2
3
4
Intent intent = new Intent(MainActivity.this, SecActivity.class);
startActivity(intent);
// 自定义的淡入淡出动画效果
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);

2> 左右滑动 效果

out_to_left.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
>
<translate
android:duration="500"
android:fromXDelta="0%p"
android:toXDelta="-100%p"
/>
</set>

in_from_right.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
>
<translate
android:duration="500"
android:fromXDelta="100%p"
android:toXDelta="0%p"
/>
</set>

在Java代码中设置效果

1
2
3
4
Intent intent = new Intent(MainActivity.this, SecActivity.class);
startActivity(intent);
// 自定义 从右向左滑动的效果
overridePendingTransition(R.anim.in_from_right, R.anim.out_to_left);

3> 酷炫的Activity切换动画,打造更好的用户体验

5.补间动画原理

四.属性动画(Property Animation)

为什么需要属性动画?

  • 补间动画 只能够作用在视图View上,即只可以对一个ButtonTextView、甚至是LinearLayout、或者其它继承自View的组件进行动画操作,但无法对非View的对象进行动画操作。
  • 补间动画 没有改变View的属性,只是改变视觉效果。
  • 补间动画 动画效果单一。

工作原理

  • 在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果。

  • 具体的工作原理逻辑如下:

实现方式

  • 实际开发中,建议使用Java代码实现属性动画:因为很多时候属性的起始值是无法提前确定的(无法使用XML设置),这就需要在Java代码里动态获取。
  • XML方式具备重用性,即将通用的动画写到XML里,可在各个界面中去重用它。

1. ValueAnimator学习

实现动画的原理:通过不断控制 值 的变化,再不断 手动 赋给对象的属性,从而实现动画效果。如下图:

详细内容请看:Android动画-ValueAnimator

2.ObjectAnimator学习

动画的原理: 通过不断控制 值 的变化,再不断 自动 赋给对象的属性,从而实现动画效果。如下图:

详细内容请看:Android动画-ObjectAnimator

3.插值器(Interpolator)和 估值器(TypeEvaluator)

详细内容请看:

4.属性动画的使用小技巧

详细内容请看:Android动画-属性动画的使用小技巧

5.ValueAnimator类 & ObjectAnimator 类的区别

对比ValueAnimator类 & ObjectAnimator 类,其实二者都属于属性动画,本质上都是一致的:先改变值,然后 赋值 给对象的属性从而实现动画效果。

区别在于:

  • ValueAnimator 类是先改变值,然后 手动赋值 给对象的属性从而实现动画;是 间接 对对象属性进行操作;
  • ObjectAnimator类是先改变值,然后 自动赋值 给对象的属性从而实现动画;是 直接 对对象属性进行操作;

参考文章

Android 逐帧动画:关于 逐帧动画 的使用都在这里了!

Android:这是一份全面 & 详细的补间动画使用教程

对Animation中的fillAfter,fillBefore,fillEnabled的理解

Android 动画:手把手带你深入了解神秘的插值器(Interpolator)

Android:属性动画的核心使用类ValueAnimator学习指南

Android:手把手带你深入了解神秘的估值器(TypeEvaluator)

Android 属性动画的ObjectAnimator类学习指南

Android 动画:这些属性动画的使用小技巧你了解吗

安卓动画学习(六)–xml实现属性动画