Android转场动画
课程目标:
1. 掌握定义转场动画的方法
学习内容:
1. 转场动画的作用
2. 哪些时机需要转场动画
3. 转动画的方法
一、 转场动画的作用
1. 转场动画可以提供视觉连续性。
二、 哪些时机需要转场动画
1. 视觉状态改变时。
a. 单个视图变化时
b. 布局变化时
c. activity跳转时
三、 转场动画的方法
1. 揭露效果(单个视图的转场动画)
a. View状态改变时(下面用一个demo演示当一个View在可见和不可见时产生揭露效果)
实践:
xml布局文件: activity_reveal.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.imooc.transitionanimation.reveal.RevealActivity">
<CheckBox
android:text="Play animation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/checkBox" />
<View
android:id="@+id/view"
android:layout_centerInParent="true"
android:background="@color/colorAccent"
android:onClick="onClick"
android:visibility="invisible"
android:layout_width="300dp"
android:layout_height="300dp"/>
<Button
android:onClick="onClick"
android:id="@+id/buttonChangeVisibility"
android:layout_width="wrap_content"
android:text="switch"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
style="?android:buttonBarButtonStyle"
android:layout_height="wrap_content" />
</RelativeLayout>
java代码: RecealActivity.java
public class RevealActivity extends AppCompatActivity {
private static final String TAG = "RevealActivity";
private View mView;
private CheckBox mPlayAnimationCheckBox;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_reveal);
mView = findViewById(R.id.view);
mPlayAnimationCheckBox = (CheckBox) findViewById(R.id.checkBox);
}
public void onClick(View view) {
final boolean playAnimation = mPlayAnimationCheckBox.isChecked();
switch (view.getId()) {
case R.id.buttonChangeVisibility:
handleChangeVisibility(playAnimation);
break;
}
}
private void handleChangeVisibility(boolean playAnimation) {
Log.d(TAG, "handleChangeVisibility() called with: playAnimation = [" + playAnimation + "]");
Log.d(TAG, "handleChangeVisibility: " + mView.isShown());
if (playAnimation) {
// 有揭露效果时View的变化效果
if (mView.isShown()) {
revealExit();
} else {
revealEnter();
}
} else {
// 没有揭露效果时View的变化效果
if (mView.isShown()) {
mView.setVisibility(View.INVISIBLE);
} else {
mView.setVisibility(View.VISIBLE);
}
}
}
// 通过揭露效果显示View
private void revealEnter() {
int w = mView.getWidth();
int h = mView.getHeight();
int cx = w;
int cy = h;
int r = (int) Math.hypot(w, h);
// 1. 创建Animator 对象(mView: 显示揭露效果的对象 cx,cy: 产生接口效果的圆心(也就是起始点) 0: 动画开始前半径为0 r: 动画结束的半径)
Animator animator = ViewAnimationUtils.createCircularReveal(mView, cx, cy, 0, r);
mView.setVisibility(View.VISIBLE);
animator.start();
}
// 通过揭露效果隐藏View
private void revealExit() {
int w = mView.getWidth();
int h = mView.getHeight();
int cx = w;
int cy = h;
int r = (int) Math.hypot(w, h);
// 1. 创建Animator 对象(mView: 显示揭露效果的对象 cx,cy: 产生接口效果的圆心(也就是起始点) r: 动画开始前半径为0 0: 动画结束的半径)
Animator animator = ViewAnimationUtils.createCircularReveal(mView, cx, cy, r, 0);
animator.setDuration(5000);
// 给Animator对象注册监听器,让其在动画结束时,View不可见
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mView.setVisibility(View.INVISIBLE);
}
});
animator.start();
}
}
2.多视图的转场动画
为了完成多视图的转场动画,Android中为我们提供了android.transition包。在这个包下
主要提供三个类:
1. Scene
2. Transition
3. TransitionManager
上面这三个类之间的关系: Scene就是场景,Transition就是转换类型,TransitionManager负责场景的转换,并将不同的转换类型结合到场景转换的过程中。
下面分别来看下这三个类:
1. Scene(场景)
我们屏幕上显示的实际上就是一个View树结构,我们看到的视图其实就是这个View树结构中所有View所处于的某种状态的瞬间,这就是一个场景(Scene)。也就是说我们可以把视图的某一个状态称为一个场景。
获取这个场景的scene对象(从布局中加载一个场景):
scene.getSceneForLayout(sceneRoot, R.layout.scene_overview, this);
参数含义:
sceneRoot:转场发生的地方
R.layout.scene_overview:组成这一场景的布局资源
this:上下文
2. Transition(转换类型)
Android中为我们预定好了一些转换效果,比如:
Fade(淡入淡出):也就是透明度变化
ChangeBounds(改变边界)
AutoTransition(默认自动使用这个效果)
获取Transition对象:
TransitionInflater.from(getBaseContext()).inflateTransition(R.transition.transition);
transition和我们之前的视图对象中Animation以及属性动画中的Animator类似,他们都可以通过xml文件和java代码定义,推荐使用xml文件。
3. TransitionManager
有了场景对象和转换类型,接着就可以进行转场动画了:
TransitionManager.go(mTargetScene, transition);
参数含义:
mTargetScene: 我们要跳转的目标场景
transition: 使用的转换类型,可以使我们自定义的,也可以用Android中提供的,如果不设置这个参数,默认使用AutoTransition这个场景。
实践:
1.使用xml文件自定义一个Transition对象,在res目录下创建一个目录/res/transition/, 然后创建一个xml文件。(/res/transition/transition.xml):
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeImageTransform android:duration="3000">
<targets android:targetId="@id/image" />
</changeImageTransform>
<fade android:duration="3000" android:startDelay="1000">
</fade>
</transitionSet>
activity布局文件( activity_scene.xml ):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_scene"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.imooc.transitionanimation.scene.SceneActivity">
<FrameLayout
android:id="@+id/scene_root"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
进入场景xml布局文件( scene_overview.xml ):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_scene"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.imooc.transitionanimation.scene.SceneActivity">
<ImageView
android:layout_width="match_parent"
android:id="@+id/image"
android:layout_height="0dp"
android:layout_weight="1"
android:src="@drawable/chang_bai"
android:transitionName="@string/pic"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<ImageButton
android:id="@+id/btnInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginTop="4dp"
android:background="?android:selectableItemBackground"
android:onClick="onClick"
android:src="@drawable/ic_info_black_24dp" />
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toStartOf="@id/btnInfo"
android:text="@string/title"
android:textAppearance="@style/TextAppearance.AppCompat.Headline" />
<TextView
android:id="@+id/tvAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tvTitle"
android:layout_marginTop="8dp"
android:text="@string/address"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
</RelativeLayout>
</LinearLayout>
退出场景xml布局文件( scene_info.xml ):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_scene"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.imooc.transitionanimation.scene.SceneActivity">
<ImageView
android:id="@+id/image"
android:layout_width="160dp"
android:layout_height="120dp"
android:src="@drawable/chang_bai" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<ImageButton
android:id="@+id/btnClose"
android:transitionName="@string/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginTop="4dp"
android:background="?android:selectableItemBackground"
android:onClick="onClick"
android:src="@drawable/ic_close_black_24dp" />
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toStartOf="@id/btnClose"
android:text="@string/title"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<TextView
android:id="@+id/tvAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tvTitle"
android:layout_marginTop="8dp"
android:text="@string/address"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<TextView
android:id="@+id/tvInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tvAddress"
android:layout_marginTop="8dp"
android:text="@string/info" />
</RelativeLayout>
</LinearLayout>
2.java代码:
public class SceneActivity extends AppCompatActivity {
private Scene mOverViewScene;
private Scene mInfoScene;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scene);
ViewGroup sceneRoot = (ViewGroup) findViewById(R.id.scene_root);
// 获取进入时的场景对象
mOverViewScene = Scene.getSceneForLayout(sceneRoot, R.layout.scene_overview, getBaseContext());
// 获取退出时的场景对象
mInfoScene = Scene.getSceneForLayout(sceneRoot, R.layout.scene_info, getBaseContext());
// 第一次加载布局时使用的场景转换,如果没有Transition参数,默认使用AutoTransition转换类型
TransitionManager.go(mOverViewScene);
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.btnInfo:
// 自定义场景转换类型transition
Transition transition = TransitionInflater.from(getBaseContext())
.inflateTransition(R.transition.transition);
TransitionManager.go(mInfoScene, transition);
break;
case R.id.btnClose:
TransitionManager.go(mOverViewScene);
break;
}
}
}
3.Activity间转场动画:
ActivityA和ActivityB之间跳转的动画涉及到3次动画:
ActivityA跳转到ActivityB时,ActivityA就会有离场动画(Exit),进入到ActivityB时ActivityB就有进场动画(Enter),当ActivityB按Back键返回到ActivityA时,ActivityA就又被再次进入,此时又是一种动画效果(Re_enter)。
使用转场动画: (ActivityOptions)
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this);
startActivity(intent, options.toBundle());
转场动画的动画类型: (Transition)
Android系统中默认的有三种:
1. Fade(淡入淡出)
2. Slide(滑动效果)
3. Explode(爆炸效果)
实践:
a. xml布局文件:
activity_first.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_first"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.imooc.transitionanimation.activity.FirstActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv1"
android:layout_width="0dp"
android:layout_height="160dp"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:onClick="onClick"
android:src="@drawable/pic1" />
<ImageView
android:id="@+id/iv2"
android:layout_width="0dp"
android:layout_height="160dp"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:onClick="onClick"
android:src="@drawable/pic2" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv3"
android:layout_width="0dp"
android:layout_height="160dp"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:onClick="onClick"
android:src="@drawable/pic3" />
<ImageView
android:id="@+id/iv4"
android:layout_width="0dp"
android:layout_height="160dp"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:onClick="onClick"
android:src="@drawable/pic4" />
</LinearLayout>
</LinearLayout>
activity_second.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_second"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.imooc.transitionanimation.activity.SecondActivity">
<ImageView
android:id="@+id/iv"
<!-- 共享元素定义的名称,要和java代码中相同 -->
android:transitionName="img"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
b. java代码:
FirstActivity.java:
public class FirstActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
}
public void onClick(View view) {
int resId = -1;
switch (view.getId()) {
case R.id.iv1:
resId = R.drawable.pic1;
break;
case R.id.iv2:
resId = R.drawable.pic2;
break;
case R.id.iv3:
resId = R.drawable.pic3;
break;
case R.id.iv4:
resId = R.drawable.pic4;
break;
}
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("resId", resId);
// 定义动画的类型(爆炸效果)
Transition transition = new Explode();
// 去除不想实现动画效果的view(这里设置的是状态栏不参加动画效果)
transition.excludeTarget(android.R.id.statusBarBackground, true);
// 设置进场动画
getWindow().setEnterTransition(transition);
// 设置离场动画
getWindow().setExitTransition(transition);
// 设置再次进入时的动画
getWindow().setReenterTransition(transition);
// 为共享元素设置特定的动画效果
getWindow().setSharedElementEnterTransition(transition);
// 定义共享元素(注意这里的名称"img"要和xml布局文件中的view transitionName属性名称一样)
Pair<View, String> shareElement = Pair.create(view, "img");
// 创建实现转场动画需要的options对象
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, shareElement);
startActivity(intent, options.toBundle());
}
}
SecondActivity.java:
public class SecondActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
int resId = getIntent().getExtras().getInt("resId");
ImageView iv = (ImageView) findViewById(R.id.iv);
iv.setTransitionName("img");
iv.setImageResource(resId);
// 下面这些含义解释如上
Transition transition = new Explode();
transition.excludeTarget(android.R.id.statusBarBackground, true);
getWindow().setEnterTransition(transition);
getWindow().setExitTransition(transition);
}
}