先说下什么是 Transition(过渡动画). Lollipop(5.0) 中 Activity 和 Fragment 的过渡动画是基于 Android 一个叫作 Transition 的新特性实现的。 初次引入这个特性是在 KitKat(4.4) 中,Transition 框架提供了一个方便的 API 来构建应用中不同 UI 状态切换时的动画。 这个框架始终围绕两个关键概念:场景和过渡。 场景 描述应用中 UI 的状态(这个定义太抽象了,下面会具体解释),过渡 就是确定两个场景转换之间的过渡动画。
具体作用:
- 可以在界面(Activity & Fragment)之间跳转的时候添加动画
- 动画共享元素之间的转换活动
- 界面中布局元素的过渡动画。
1. Transitions between Activity & Fragment(Content Transition)
在 Android 5.0 中, 切换 Activitys 或者 Fragments 时可以使用 Transitions 来构建精致的转场动画。虽然在之前的版本中已经引入 Activity 和 Fragment 的切换动画(通过 Activity#overridePendingTransition() 和 FragmentTransaction#setCustomAnimation() 方法实现),但是动画的对象只能是Activity/Fragment整体。而新的 API 将这个特性延伸,可以协调 Activity/Fragment 中每一个 view ,为其设置单独的的进入和退出 transition,轻松搞定流畅的屏幕切换动作。
android.transition预定义了三种过渡动画:Explode,Slide和Fade。
Explode | Slide | Fade |
---|---|---|
从中心移入或移出 | 从边缘移入或移出 | 调整透明度产生渐变 |
不同的场景下,我们可以为同一个界面设置不同效果的过渡动画,这里解释下场景的意思:
假设 A 和 B 是两个 Activity,通过 A 来启动 B。 A 叫做 "调用Activity"(调用 startActivity() 的那个) B 就是 "被调用Activity"
根据上述情景,可以为activity划分出4种场景的动画:
- Activity A 的 退出动画(ExitTransition ),即 A 启动 B 时 A 中 View 的动画
- Activity B 的 进入动画(EnterTransition ),即 A 启动 B 时 B 中 View 的动画
- Activity B 的 返回动画(ReturnTransition),即 B 返回 A 时 B 中 View 的动画
- Activity A 的 重入动画(ReenterTransition), 即 B 返回 A 时 A 中 View 的动画
实现方法
接下来我们实际演示下为Activity添加过场动画的实现步骤,可通过xml和代码两种方式实现,以添加Fade动画为例:
- 如果是xml方式实现,首先在/res下创建transition文件夹。
res/transition/slide_from_right
<?xml version = 1.0 encoding = "utf-8"?>
<transitionSet xmls:android = "http://schemas.android.com/apk/res/android">
<slide duration = "500"
slideEage = "left"/>
</transitionSet>
然后在values/styles.xml中创建带动画的主题,再加到Manifest.xml中就可以了:
<style name="TransitionAppTheme" parent="AppTheme">
<item name="android:windowContentTransitions">true</item>
<item name="android:windowEnterTransition">@transition/slide_from_right</item>
<item name="android:windowExitTransition">@transition/slide_from_right</item>
<!--避免跳转的Activity动画重叠,则下面两个属性设为false-->
<item name="android:windowAllowEnterTransitionOverlap">false</item>
<item name="android:windowAllowReturnTransitionOverlap">false</item>
</style>
- 如果直接用代码实现,可以如下实现
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
... ...
Slide slideTracition = newSlide();
slideTracition.setSlideEdge(Gravity.LEFT);
slideTracition.setDuration(getResources().getInteger(R.integer.anim_duration_long));
//也可以直接取res中的transition资源
//Transition slideTracition = TransitionInflater.from(this).inflateTransition(R.transition.slide_from_left);
getWindow().setEnterTransition(slideTracition);
getWindow().setExitTransition(slideTracition);
... ...
}
最后,启动动画
//跳转
Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(activity).toBundle()
startActivity(i, bundle);
//返回
finshAfterTransition();
上面我们只添加了EnterTransition和ExitTransition,如果未设置ReturnTransition和ReenterTransition的话,后两者分别为前两者的反向动画。
EnterTransition < - > ReturnTransition
ExitTransition < - > ReenterTransition
2.Share elements between Activity(元素共享)
共享元素过渡动画的背后是通过过渡动画将两个不同布局中的不同view关联起来。Transition框架知道用适当的动画向用户展示从一个view向另外 一个view过度。请记住:共享元素过渡的过程中,view并没有真正从一个布局跑到另外一个布局,整个过程基本都是在后一个布局中完成的。
image
元素共享的实现
- 允许过渡动画
需要在/res/style.xml添加
<item name="android:windowContentTransitions">true</item>
- 在对应的xml文件指定TransitionName属性
例如,我们指定activityA中的ImageView和activityB中的TextView为共享元素:
<ImageView
android:id="@+id/square_blue"
style="@style/MaterialAnimations.Icon.Big"
android:src="@drawable/circle_24dp"
android:transitionName="@string/square_blue_name" />
<TextView
android:id="@+id/title"
style="@style/MaterialAnimations.TextAppearance.Title.Invers"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:text="@{sharedSample.name}"
android:transitionName="@string/sample_blue_title" />
注意:这里必须为共享的俩个元素指定同一个TransitionName,不然不会出现共享效果
- 启动Activity
Intent intent = new Intent(activity,target);
ActivityOptionCompat option = ActiviyoptionCampat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(viewHolder.binding.sampleIcon, activity.getString(R.string.square_blue_name)),
new Pair<View, String>(viewHolder.binding.sampleName, activity.getString(R.string.sample_blue_title)));
startActivity(intent,option.toBundle());
效果如下:
共享元素动画的效果设置
我们可以发现对于转场动画(Content transitions) 是根据每个过渡视图的可见性变化来调节的,而共享元素 transition 是根据每个共享元素视图的位置,大小和外观的变化来调节的。与转场动画类似,从 API 21 开始,框架提供了 几个自定义共享元素场景切换动画的 Transition 实现。
- ChangeBounds - 捕获共享元素布局边界根据不同构造动画。 ChangeBounds 在共享元素 Transition 中经常使用,大多数共享元素在两个 Activity/Fragment 间会有大小 或/和 位置不同。
- ChangeTransform - 捕获共享元素缩放和角度, 根据不同构建动画。
- ChangeClipBounds - 捕获共享元素的 clip bounds (剪辑边界) ,根据不同构建动画。
- ChangeImageTransform - 捕获共享元素 ImageView 的 变换矩阵( transform matrices) ,根据不同构建动画。结合 ChangeBounds, 可以让 ImageView 无缝的改变大小,形状和 ImageView.ScaleType 。
设置代码:
Slide slide = new Slide();
slide.setDuration(500);
ChangeBounds changeBounds = new ChangeBounds();
changeBounds.setDuration(500);
getWindow().setEnterTransition(slide);
getWindow().setSharedElementEnterTransition(changeBounds);
前面的代码我们并没有设置共享元素动画,因为如果我们的Theme使用的是 Material主题,当设置共享元素后会默认添加动画效果。
过渡动画以及共享元素在Fragment之间的实现
fragment使用过渡动画和共享元素,与Activity大同小异,也是直接添加Transition对象之后启动,主要是启动方法有所不同
private void addNextFragment(Sample sample, ImageView blue, boolean b) {
SharedElementFragment2 elementFragment2 = SharedElementFragment2.newInstance(sample);
Slide slide = new Slide();
slide.setDuration(getResources().getInteger(R.integer.anim_duration_medium));
slide.setSlideEdge(Gravity.RIGHT);
ChangeBounds changeBounds = new ChangeBounds();
changeBounds.setDuration(getResources().getInteger(R.integer.anim_duration_medium));
elementFragment2.setEnterTransition(slide);
elementFragment2.setAllowEnterTransitionOverlap(b);
elementFragment2.setAllowReturnTransitionOverlap(b);
elementFragment2.setSharedElementEnterTransition(changeBounds);
getFragmentManager().beginTransaction().replace(R.id.sample2_content, elementFragment2).addToBackStack(null).addSharedElement(blue, getString(R.string.square_blue_name)).commit();
}
效果如下:
3.TransitionManager 控制动画
TransitionManager是个很好用的工具,使用TransitionManager,我们给view添加一些简单属性动画的时候只需要得到这个view的根布局,然后设置下view动画之后的状态就可以了,TransitionManager会自动为每个view添加预设置好的属性动画,我们甚至可以用它对一个布局内的一组多个view一块儿添加动画,从而实现较为复杂的联动效果。事实上UI场景切换的效果就是通过它来实现的。
public class ExampleActivity extends Activity implements View.OnClickListener {
private ViewGroup mRootView;
private View mRedBox, mGreenBox, mBlueBox, mBlackBox;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//mRootView是要添加动画view的根布局
mRootView = (ViewGroup) findViewById(R.id.layout_root_view);
mRootView.setOnClickListener(this);
mRedBox = findViewById(R.id.red_box);
mGreenBox = findViewById(R.id.green_box);
mBlueBox = findViewById(R.id.blue_box);
mBlackBox = findViewById(R.id.black_box);
}
@Override
public void onClick(View v) {
TransitionManager.beginDelayedTransition(mRootView, new Fade());
toggleVisibility(mRedBox, mGreenBox, mBlueBox, mBlackBox);
}
private static void toggleVisibility(View... views) {
for (View view : views) {
boolean isVisible = view.getVisibility() == View.VISIBLE;
view.setVisibility(isVisible ? View.INVISIBLE : View.VISIBLE);
}
}
}
上面的例子我们为一组四个色块添加了Fade效果的动画,同样可以设置成Slide或者Explode,效果如下:
对于一些更为复杂的联动动画,TransitionManager还为我们提供了一种类似于切换布局就可以完成动画的方案,极大的简化了我们实现复杂动画
实现步骤如下:
- 定义需要切换 layout xml页面;
- 调用 Scene.getSceneForLayout() 保存每个Layout;
- 调用 TransitionManager.go(scene1, new ChangeBounds()) 切换。
代码示例:
private void setupLayout() {
scene0 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene0, this);
scene1 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene1, this);
scene2 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene2, this);
scene3 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene3, this);
scene4 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene4, this);
binding.sample3Button1.setOnClickListener(this);
binding.sample3Button2.setOnClickListener(this);
binding.sample3Button3.setOnClickListener(this);
binding.sample3Button4.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.sample3_button1:
TransitionManager.go(scene1, new ChangeBounds());
break;
case R.id.sample3_button2:
TransitionManager.go(scene2, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds));
break;
case R.id.sample3_button3:
TransitionManager.go(scene3,TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds_sequential));
break;
case R.id.sample3_button4:
TransitionManager.go(scene3,TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds_sequential_with_interpolators));
break;
}
}
4.CircularReveal 显示或隐藏 的效果
ViewAnimationUtils.createCircularReveal()
当您显示或隐藏一组 UI 元素时,Circular Reveal 可为用户提供视觉连续性
参考说明:
Animator createCircularReveal (View view, // 将要变化的 View
int centerX, // 动画圆的中心的x坐标
int centerY, // 动画圆的中心的y坐标
float startRadius, // 动画圆的起始半径
float endRadius // 动画圆的结束半径
)
显示View:
private void animShow() {
View myView = findViewById(R.id.my_view);
// 从 View 的中心开始
int cx = (myView.getLeft() + myView.getRight()) / 2;
int cy = (myView.getTop() + myView.getBottom()) / 2;
int finalRadius = Math.max(myView.getWidth(), myView.getHeight());
//为此视图创建动画设计(起始半径为零)
Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
// 使视图可见并启动动画
myView.setVisibility(View.VISIBLE);
anim.start();
}
隐藏View:
private void animHide() {
final View myView = findViewById(R.id.my_view);
int cx = (myView.getLeft() + myView.getRight()) / 2;
int cy = (myView.getTop() + myView.getBottom()) / 2;
int initialRadius = myView.getWidth();
// 半径 从 viewWidth -> 0
Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
myView.setVisibility(View.INVISIBLE);
}
});
anim.start();
}
以上效果都只支持 API 23 以上,所以在我们常用的 APP 中都还不常见,但是效果真的很不错,很值得研究下。
参考及拓展阅读:
在 Activity 和 Fragment 中使用 Transition (part 1)
深入理解Content Transition (part 2)
深入理解 Shared Element Transition (part 3a)
延迟共享元素的过渡动画 (part 3b)
Android共享元素转场动画兼容实践
【Transition】Android炫酷的Activity切换效果,共享元素
网友评论