Android基础——Tween动画、Drawable动画、Property动画、MaterialDesign动画、Trasition动画

项目主页

这里写图片描述

项目结构

项目结构清晰,以介绍的模块对应起来

这里写图片描述

BaseActivity

这里会发现一个奇怪的Activity,每个Activity都继承这个BaseActivity,其作用就是增加标题和返回键的功能

这里写图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
}

Tween动画

这里写图片描述

Tween动画比较简单,我们常说的平移、旋转、缩放、透明度这四个动画,其中可以将每个动画都搭配起来成为集合,Tween动画的展示形式有两种,一种通过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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
public class TweenActivity extends BaseActivity implements View.OnClickListener {
private ImageView rectangle_red;
private Switch isXml;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tween);
rectangle_red = (ImageView) findViewById(R.id.rectangle_red);
isXml = (Switch) findViewById(R.id.isXml);
findViewById(R.id.translate).setOnClickListener(this);
findViewById(R.id.rotate).setOnClickListener(this);
findViewById(R.id.scale).setOnClickListener(this);
findViewById(R.id.alpha).setOnClickListener(this);
findViewById(R.id.set).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.translate:
if (isXml.isChecked()) {
Animation anim = AnimationUtils.loadAnimation(this, R.anim.tween_translate);
rectangle_red.startAnimation(anim);
return;
}
//平移动画
TranslateAnimation translateAnimation = new TranslateAnimation(0, 100, 0, 100);
translateAnimation.setRepeatMode(Animation.REVERSE);
translateAnimation.setFillAfter(true);
translateAnimation.setDuration(2000);
translateAnimation.setRepeatCount(1);
rectangle_red.startAnimation(translateAnimation);
break;
case R.id.rotate:
if (isXml.isChecked()) {
Animation anim = AnimationUtils.loadAnimation(this, R.anim.tween_rotate);
rectangle_red.startAnimation(anim);
return;
}
//旋转动画
RotateAnimation rotateAnimation = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, Animation.RELATIVE_TO_SELF);
rotateAnimation.setRepeatMode(Animation.REVERSE);
rotateAnimation.setFillAfter(true);
rotateAnimation.setDuration(2000);
rotateAnimation.setRepeatCount(1);
rectangle_red.startAnimation(rotateAnimation);
break;
case R.id.scale:
if (isXml.isChecked()) {
Animation anim = AnimationUtils.loadAnimation(this, R.anim.tween_scale);
rectangle_red.startAnimation(anim);
return;
}
//缩放动画
ScaleAnimation scaleAnimation = new ScaleAnimation(1, 2, 1, 2, Animation.RELATIVE_TO_SELF, Animation.RELATIVE_TO_SELF);
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleAnimation.setFillAfter(true);
scaleAnimation.setDuration(2000);
scaleAnimation.setRepeatCount(1);
rectangle_red.startAnimation(scaleAnimation);
break;
case R.id.alpha:
if (isXml.isChecked()) {
Animation anim = AnimationUtils.loadAnimation(this, R.anim.tween_alpha);
rectangle_red.startAnimation(anim);
return;
}
//透明度动画
AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0.5f);
alphaAnimation.setRepeatMode(Animation.REVERSE);
alphaAnimation.setFillAfter(true);
alphaAnimation.setDuration(2000);
alphaAnimation.setRepeatCount(1);
rectangle_red.startAnimation(alphaAnimation);
break;
case R.id.set:
if (isXml.isChecked()) {
Animation anim = AnimationUtils.loadAnimation(this, R.anim.tween_set);
rectangle_red.startAnimation(anim);
return;
}
//动画集合
ScaleAnimation scaleAnim = new ScaleAnimation(1, 2, 1, 2, Animation.RELATIVE_TO_SELF, Animation.RELATIVE_TO_SELF);
scaleAnim.setRepeatMode(Animation.REVERSE);
scaleAnim.setFillAfter(true);
scaleAnim.setDuration(2000);
scaleAnim.setRepeatCount(1);
AlphaAnimation alphaAnim = new AlphaAnimation(1, 0.5f);
alphaAnim.setRepeatMode(Animation.REVERSE);
alphaAnim.setFillAfter(true);
alphaAnim.setDuration(2000);
alphaAnim.setRepeatCount(1);
AnimationSet set = new AnimationSet(true);
set.addAnimation(scaleAnim);
set.addAnimation(alphaAnim);
rectangle_red.startAnimation(set);
break;
}
}
}

Xml形式

如果是Xml形式的话,就必须在res目录下的anim目录下指定对应的动画xml文件,具体的内容可以查看源码

这里写图片描述

Drawable动画

这里写图片描述

Drawable也可以称为帧动画,就是通过一帧一帧的图片展示出来的动画,代码很简单,只需要开启动画就可以了

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
public class DrawableActivity extends BaseActivity implements View.OnClickListener {
private AnimationDrawable background;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drawable);
findViewById(R.id.start).setOnClickListener(this);
ImageView iv_arrow = (ImageView) findViewById(R.id.iv_arrow);
iv_arrow.setBackgroundResource(R.drawable.drawable_anim);
background = (AnimationDrawable) iv_arrow.getBackground();
}
@Override
public void onClick(View v) {
background.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (background.isRunning()) {
background.stop();
}
}
}

在布局文件中使用background属性即可

1
2
3
4
5
6
<ImageView
android:id="@+id/iv_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/drawable_anim" />

这个drawable_anim就是一长串的动画图片的声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item android:duration="16">
<bitmap android:src="@drawable/ic_done_anim_000" />
</item>
<item android:duration="16">
<bitmap android:src="@drawable/ic_done_anim_001" />
</item>
<item android:duration="16">
<bitmap android:src="@drawable/ic_done_anim_002" />
</item>
<item android:duration="16">
<bitmap android:src="@drawable/ic_done_anim_003" />
</item>
<item android:duration="16">
<bitmap android:src="@drawable/ic_done_anim_004" />
</item>
<item android:duration="16">
<bitmap android:src="@drawable/ic_done_anim_005" />
</item>
......
</animation-list>

Property动画

这里写图片描述

Property动画分为两种

  • ObjectAnimator:ObjectAnimator将View的属性就行修改,使其产生动画,比如x坐标y坐标修改,就能形成平移
  • ValueAnimator:ValueAnimator会分配一个区间的值,随着值的增长或者下降,使用其产生的值进行更新动画
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
public class PropertyActivity extends BaseActivity implements View.OnClickListener {
private ImageView rectangle_red;
private Switch isXml;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_property);
rectangle_red = (ImageView) findViewById(R.id.rectangle_red);
isXml = (Switch) findViewById(R.id.isXml);
findViewById(R.id.translate).setOnClickListener(this);
findViewById(R.id.rotate).setOnClickListener(this);
findViewById(R.id.scale).setOnClickListener(this);
findViewById(R.id.alpha).setOnClickListener(this);
findViewById(R.id.set).setOnClickListener(this);
findViewById(R.id.setWay2).setOnClickListener(this);
findViewById(R.id.translate2).setOnClickListener(this);
findViewById(R.id.rotate2).setOnClickListener(this);
findViewById(R.id.scale2).setOnClickListener(this);
findViewById(R.id.alpha2).setOnClickListener(this);
findViewById(R.id.set2).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.translate:
if (isXml.isChecked()) {
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.value_translate);
anim.setTarget(rectangle_red);
anim.start();
return;
}
ObjectAnimator.ofFloat(rectangle_red, "translationX", 0.0F, 150.0F)
.setDuration(1000)
.start();
break;
case R.id.rotate:
if (isXml.isChecked()) {
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.value_rotate);
anim.setTarget(rectangle_red);
anim.start();
return;
}
ObjectAnimator.ofFloat(rectangle_red, "rotationX", 0.0F, 360.0F)
.setDuration(1000)
.start();
break;
case R.id.scale:
if (isXml.isChecked()) {
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.value_scale);
anim.setTarget(rectangle_red);
anim.start();
return;
}
ObjectAnimator.ofFloat(rectangle_red, "scaleX", 1.0F, 3.0F)
.setDuration(1000)
.start();
break;
case R.id.alpha:
if (isXml.isChecked()) {
Toast.makeText(this, "这个不行", Toast.LENGTH_SHORT).show();
return;
}
ObjectAnimator.ofFloat(rectangle_red, "alpha", 1F, 0.5F)
.setDuration(1000)
.start();
break;
case R.id.set:
//动画集合
if (isXml.isChecked()) {
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.value_set);
anim.setTarget(rectangle_red);
anim.start();
return;
}
PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofFloat("scaleX", 1f, 0f, 1f);
PropertyValuesHolder pvhScaleY = PropertyValuesHolder.ofFloat("scaleY", 1f, 0f, 1f);
ObjectAnimator customAnim = ObjectAnimator.ofPropertyValuesHolder(rectangle_red, pvhScaleX, pvhScaleY);
customAnim.setDuration(4000);
customAnim.start();
break;
case R.id.setWay2:
//动画集合2
if (isXml.isChecked()) {
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.value_set);
anim.setTarget(rectangle_red);
anim.start();
return;
}
ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(rectangle_red, "translationX", 0.0F, 150.0F).setDuration(1000);
ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(rectangle_red, "translationY", 0.0F, 150.0F).setDuration(1000);
ObjectAnimator objectAnimator3 = ObjectAnimator.ofFloat(rectangle_red, "rotationX", 0.0F, 360.0F).setDuration(1000);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(3000);
// 随机设置
animatorSet.play(objectAnimator1)
.with(objectAnimator2)
.after(objectAnimator3);
// // 一起播放
// animatorSet.playTogether(objectAnimator1, objectAnimator2, objectAnimator3);
// // 顺序播放
// animatorSet.playSequentially(objectAnimator1, objectAnimator2, objectAnimator3);
animatorSet.start();
break;
case R.id.translate2:
startValueAnimator(0);
break;
case R.id.rotate2:
startValueAnimator(1);
break;
case R.id.scale2:
startValueAnimator(2);
break;
case R.id.alpha2:
startValueAnimator(3);
break;
case R.id.set2:
startValueAnimator(4);
break;
}
}
/**
* ValueAnimator
*/
public void startValueAnimator(final int id) {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(150)
.setDuration(1000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (id == 0) {
rectangle_red.setTranslationX((Float) animation.getAnimatedValue());
} else if (id == 1) {
rectangle_red.setRotation((Float) animation.getAnimatedValue());
} else if (id == 2) {
rectangle_red.setScaleX((Float) animation.getAnimatedValue());
} else if (id == 3) {
rectangle_red.setAlpha((Float) animation.getAnimatedValue());
} else if (id == 4) {
rectangle_red.setTranslationX((Float) animation.getAnimatedValue());
rectangle_red.setScaleX((Float) animation.getAnimatedValue());
rectangle_red.setAlpha((Float) animation.getAnimatedValue());
}
}
});
valueAnimator.start();
}
}

MaterialDesign动画

这里写图片描述

MaterialDesign动画分为两种

  • 水波纹动画
  • 揭露动画

水波纹动画

水波纹就是通过水波纹动画在xml上的编写,然后通过View设置background即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<Button
android:id="@+id/ripple"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/materia_ripple"
android:text="Ripple" />
<Button
android:id="@+id/ripple1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/materia_ripple1"
android:text="Ripple1" />
<Button
android:id="@+id/ripple2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/materia_ripple2"
android:text="Ripple2" />

materia_ripple.xml

1
2
3
4
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccent">
</ripple>

materia_ripple1.xml

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccent">
<item
android:id="@android:id/mask"
android:drawable="@android:color/holo_green_dark"/>
</ripple>

materia_ripple2.xml

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccent">
<item
android:id="@android:id/mask"
android:drawable="@android:color/holo_green_dark"/>
<item android:drawable="@color/colorPrimary"/>
</ripple>

揭露动画

揭露动画只要在通过代码生成即可

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
public class MaterialDesignActivity extends BaseActivity implements View.OnClickListener {
private ImageView show_pic;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_material_design);
show_pic = (ImageView) findViewById(R.id.show_pic);
findViewById(R.id.reveal).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.reveal:
//直径
int d = Math.max(show_pic.getHeight(), show_pic.getWidth()) * 2;
//参数1:view 参数2:x坐标 参数3:y坐标 参数4:动画开始的半径 参数:动画结束的半径
Animator animator = ViewAnimationUtils.createCircularReveal(show_pic, 0, 0, 0, d);
animator.setDuration(1000);
animator.start();
break;
}
}
}

Transition动画

这里写图片描述

Transition动画是目前比较流行的动画,包含以下内容

  • 转场动画
  • 共享元素动画

转场动画

系统默认为我们提供了三种转场动画,其实现步骤如下

1、在需要跳转的界面中,启动Activity的时候调用新的API

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
public class TransitionActivity extends BaseActivity implements View.OnClickListener {
private Intent mIntent = new Intent();
private Switch isXml;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transition);
isXml = (Switch) findViewById(R.id.isXml);
findViewById(R.id.explode).setOnClickListener(this);
findViewById(R.id.slide).setOnClickListener(this);
findViewById(R.id.fade).setOnClickListener(this);
}
/**
* 启动Activity
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.explode:
mIntent.setClass(this, TransitionToThisActivity.class);
mIntent.putExtra("transition", "explode");
mIntent.putExtra("isXml", isXml.isChecked());
startActivity(mIntent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
break;
case R.id.slide:
mIntent.setClass(this, TransitionToThisActivity.class);
mIntent.putExtra("transition", "slide");
mIntent.putExtra("isXml", isXml.isChecked());
startActivity(mIntent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
break;
case R.id.fade:
mIntent.setClass(this, TransitionToThisActivity.class);
mIntent.putExtra("transition", "fade");
mIntent.putExtra("isXml", isXml.isChecked());
startActivity(mIntent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
break;
}
}
}

2、在跳转过去的界面中,设置进入动画和退出动画即可

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class TransitionToThisActivity extends BaseActivity {
private String transition;
private TextView textView;
private boolean isXml;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transition_to_this);
transition = getIntent().getStringExtra("transition");
isXml = getIntent().getBooleanExtra("isXml", false);
initAnimation();
}
/**
* 设置进入动画和退出动画
*/
private void initAnimation() {
textView = (TextView) findViewById(R.id.share_text);
textView.setText(transition);
switch (transition) {
case "explode":
if (isXml) {
Explode explode = (Explode) TransitionInflater.from(this).inflateTransition(R.transition.explode);
explode.setDuration(1000L);
getWindow().setEnterTransition(explode);
return;
}
Explode explode = new Explode();
explode.setDuration(1000L);
getWindow().setEnterTransition(explode);
break;
case "slide":
if (isXml) {
Slide slide = (Slide) TransitionInflater.from(this).inflateTransition(R.transition.slide);
slide.setDuration(1000L);
getWindow().setEnterTransition(slide);
return;
}
Slide slide = new Slide(Gravity.BOTTOM);
slide.setDuration(1000L);
getWindow().setEnterTransition(slide);
break;
case "fade":
if (isXml) {
Fade fade = (Fade) TransitionInflater.from(this).inflateTransition(R.transition.fade);
fade.setDuration(1000L);
getWindow().setEnterTransition(fade);
return;
}
Fade fade = new Fade();
fade.setDuration(1000L);
getWindow().setEnterTransition(fade);
break;
default:
break;
}
}
}

共享元素

这里写图片描述

共享元素,其实现步骤如下

1、布局文件中设置view元素的共享字段,通过transitionName声明共享元素的字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/rectangle_red"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@drawable/rectangle_red"
android:transitionName="share" />
<TextView
android:id="@+id/share_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:gravity="center"
android:text="Explode"
android:transitionName="share_text" />
</LinearLayout>

2、启动Activity指定所有共享view元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mIntent.setClass(this, TransitionToThisActivity.class);
mIntent.putExtra("transition", "share");
//5.0以上兼容的API
ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(this
, Pair.create(findViewById(R.id.rectangle_red), "share")
, Pair.create(findViewById(R.id.share_text), "share_text"));
//5.0以下兼容的API
ActivityOptionsCompat activityOptionsCompat1 = ActivityOptionsCompat.makeSceneTransitionAnimation(this
, Pair.create(findViewById(R.id.rectangle_red), "share")
, Pair.create(findViewById(R.id.share_text), "share_text"));
startActivity(mIntent, transitionActivityOptions.toBundle());

源码下载

源码下载

坚持原创技术分享,您的支持将鼓励我继续创作!