Transition FrameWork
Android Transition Framework可以实现三种效果:
- 不同Activity之间切换时,Activityc的内容(contentView)转场动画
- 不同Activity之间切换时,如果使用了Shared Element动画,也可以使用Transition FrameWork来实现不同的过渡动画效果
- 同一个Activity内View变化的过渡动画(Scene)
1. Activity之间切换的过渡动画
通过这种方法可以使activity切换时,他们的布局内容有过度动画
当从Activity A
切换到Activity B
的时候,Activity布局的内容会按照预先定义好的动画来执行过渡动画。
在android.transition
包中,已经有三种现成的动画可以用:Explode,Slide和Fade。
所有这些过渡都会跟踪activity布局中可见的目标Views,驱动这些Views按照过渡的规则产生响应的动画效果。
Explode | Slide | Fade |
---|---|---|
|
|
|
你可以在xml中创建这些过渡效果,也可以通过代码来创建。对于Fade过渡效果来说,它看起来是这样子的:
xml中创建
过渡效果定义在xml中,目录是res/transition
res/transition/activity_fade.xml
<?xml version="1.0" encoding="utf-8"?>
<fade xmlns:android="http://schemas.android.com/apk/res/"
android:duration="1000"/>
res/transition/activity_slide.xml
<?xml version="1.0" encoding="utf-8"?>
<slide xmlns:android="http://schemas.android.com/apk/res/"
android:duration="1000"/>
要使用这些xml'中定义的过渡动画,你需要使用TransitionInflater
来实例化它们。
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transition);
setupWindowAnimations();
} private void setupWindowAnimations() {
Slide slide = TransitionInflater.from(this).inflateTransition(R.transition.activity_slide);
getWindow().setExitTransition(slide);
}
TransitionActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transition);
setupWindowAnimations();
} private void setupWindowAnimations() {
Fade fade = TransitionInflater.from(this).inflateTransition(R.transition.activity_fade);
getWindow().setEnterTransition(fade);
}
在代码中创建
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transition);
setupWindowAnimations();
} private void setupWindowAnimations() {
Slide slide = new Slide();
slide.setDuration(1000);
getWindow().setExitTransition(slide);
}
TransitionActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transition);
setupWindowAnimations();
} private void setupWindowAnimations() {
Fade fade = new Fade();
fade.setDuration(1000);
getWindow().setEnterTransition(fade);
}
不管哪种创建方法都会产生如下的效果:
那么这里面一步一步的到底发生了什么:
Activity A 启动 Activity B
Transition Framework 发现 A 中定义了Exit Transition (slide) 然后就会对它的所有可见的View使用这个过渡动画.
- Transition Framework 发现 B 中定义了Enter Transition (fade) 然后机会对它所有可见的Views使用这个过渡动画.
- On Back Pressed(按返回键) Transition Framework 会执行把 Enter and Exit过渡动画反过来执行(但是如果定义了
returnTransition
和reenterTransition
,那么就会执行这些定义的动画)
译注:
- Exit Transition:可以理解为activity进入后台的过渡动画
- Enter Transition:可以理解为创建activity并显示时的过渡动画
- Return Transition:可以理解为销毁activity时的过渡动画
- Reenter Transition:可以理解为activity从后台进入前台时的过渡动画
- 要使这些过渡动画生效,我们需要调用
startActivity(intent,bundle)
方法来启动activity。bundle
需要通过ActivityOptionsCompat.makeSceneTransitionAnimation().toBundle()
的方式来生成
ReturnTransition & ReenterTransition
Return and Reenter Transitions是与进入和退出动画相对应的.
- EnterTransition <--> ReturnTransition
- ExitTransition <--> ReenterTransition
如果Return or Reenter没有创建, Android 会把Enter and Exit Transitions反过来执行. 但是如果你创建了Return or Reenter,那Android就会执行你创建的动画,并且这些动画可以不同.
我们可以修改下前面Fade的例子,给 TransitionActivity
创建 ReturnTransition
。这里我们就拿Slide过渡效果来举例子。这样,如果我们从B返回到A的时候,B就会执行一个Slide的过渡效果。
TransitionActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transition);
setupWindowAnimations();
} private void setupWindowAnimations() {
Fade fade = new Fade();
fade.setDuration(1000);
getWindow().setEnterTransition(fade); Slide slide = new Slide();
slide.setDuration(1000);
getWindow().setReturnTransition(slide);
}
可以看到,如果没有创建Return Transition,退出的时候会执行Enter Transtion相反的动画。如果创建了Return Transition,那么就会执行这个创建的动画效果。
没有Return Transition | 有Return Transition |
---|---|
Enter: Fade In |
Enter: Fade In |
Exit: Fade Out |
Exit: Slide out |
transition_fade
|
transition_fade2
|
2. Activity之间共享元素(Share Elements)
这里的思想就是通过动画的形式将两个不同布局中的两个不同的View联系起来。
然后Transition framework就会在用户从一个View切换到另一个View的时候给用户展现一些必要的动画。
但你要记住:发生动画的View并不是从一个布局中移动到另一个布局。他们是两个独立的View。
a) 设置Window Content Transition属性
你需要在app的 styles.xml
中进行设置.[译]我没有设置也没问题
values/styles.xml
<style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar">
...
<item name="android:windowContentTransitions">true</item
...
</style>
如果你愿意的话,你也可以给整个app设置一个默认的转场动画和共享元素动画。
<style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar">
...
<!-- specify enter and exit transitions -->
<item name="android:windowEnterTransition">@transition/explode</item>
<item name="android:windowExitTransition">@transition/explode</item> <!-- specify shared element transitions -->
<item name="android:windowSharedElementEnterTransition">@transition/changebounds</item>
<item name="android:windowSharedElementExitTransition">@transition/changebounds</item>
...
</style>
b)设置相同的transition name
为了使共享元素动画生效,你需要给共享元素的两个View设置相同的android:transitionName
属性值。不过他们的id和其他属性可以不同。
layout/activity_a.xml
<ImageView
android:id="@+id/small_blue_icon"
style="@style/MaterialAnimations.Icon.Small"
android:src="@drawable/circle"
android:transitionName="@string/blue_name" />
layout/activity_b.xml
<ImageView
android:id="@+id/big_blue_icon"
style="@style/MaterialAnimations.Icon.Big"
android:src="@drawable/circle"
android:transitionName="@string/blue_name" />
c) 用共享元素来启动activity
使用ActivityOptions.makeSceneTransitionAnimation()
方法指定要共享元素的View和android:transitionName
属性的值
MainActivity.java
blueIconImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, SharedElementActivity.class); View sharedView = blueIconImageView;
String transitionName = getString(R.string.blue_name); ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, sharedView, transitionName);
startActivity(i, transitionActivityOptions.toBundle());
}
});
这样就可以有下面漂亮的过渡效果了:
可以看到, Transition framework 创建并执行了一个动画。动画的视觉效果就是一个View从一个activity移动到另一个activity中并伴随着形状的变化。
在fragment之间实现Shared elements过渡动画
这个activity中的做法几乎是一样的。
a) 和b)完全一样, 只有c)有点区别
a) 设置Window Content Transition属性
values/styles.xml
<style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar">
...
<item name="android:windowContentTransitions">true</item>
...
</style>
b) 设置相同的transition name
layout/fragment_a.xml
<ImageView
android:id="@+id/small_blue_icon"
style="@style/MaterialAnimations.Icon.Small"
android:src="@drawable/circle"
android:transitionName="@string/blue_name" />
layout/fragment_b.xml
<ImageView
android:id="@+id/big_blue_icon"
style="@style/MaterialAnimations.Icon.Big"
android:src="@drawable/circle"
android:transitionName="@string/blue_name" />
c) 启动带有共享元素的fragment
在你使用FragmentTransaction
启动fragment的时候,你需要同时带上共享元素过渡动画的先关信息。
FragmentB fragmentB = FragmentB.newInstance(sample); // Defines enter transition for all fragment views
Slide slideTransition = new Slide(Gravity.RIGHT);
slideTransition.setDuration(1000);
sharedElementFragment2.setEnterTransition(slideTransition); // Defines enter transition only for shared element
ChangeBounds changeBoundsTransition = TransitionInflater.from(this).inflateTransition(R.transition.change_bounds);
fragmentB.setSharedElementEnterTransition(changeBoundsTransition); getFragmentManager().beginTransaction()
.replace(R.id.content, fragmentB)
.addSharedElement(blueView, getString(R.string.blue_name))
.commit();
最终的效果就是这样的:
允许过渡效果之间的重叠
You can define if enter and exit transitions can overlap each other.
你可以设置一个activity的退出效果和另一个activity的进入效果产生重叠部分。
Android文档这么说的:
当设置为true,enter transition会立马执行>
当设置为false,enter transition会等到退出exit transition结束后再执行.
这对Fragments和Activities的共享元素过渡效果都是有用的。
FragmentB fragmentB = FragmentB.newInstance(sample); // Defines enter transition for all fragment views
Slide slideTransition = new Slide(Gravity.RIGHT);
slideTransition.setDuration(1000);
sharedElementFragment2.setEnterTransition(slideTransition); // Defines enter transition only for shared element
ChangeBounds changeBoundsTransition = TransitionInflater.from(this).inflateTransition(R.transition.change_bounds);
fragmentB.setSharedElementEnterTransition(changeBoundsTransition); // Prevent transitions for overlapping
fragmentB.setAllowEnterTransitionOverlap(overlap);
fragmentB.setAllowReturnTransitionOverlap(overlap); getFragmentManager().beginTransaction()
.replace(R.id.content, fragmentB)
.addSharedElement(blueView, getString(R.string.blue_name))
.commit();
可以看到,效果还是挺明显的:
Overlap True | Overlap False |
---|---|
Fragment_2 出现在Fragment_1的上面 | Fragment_2 等到Fragment_1消失后才出现 |
shared_element_overlap
|
shared_element_no_overlap
|
3. 布局元素动画
Scenes
也可以用来驱动一个activity中的布局发生变化时,让其中的View产生过渡动画。
过渡效果发生在场景之间。场景就是一个定义了静态UI的普通布局。Transition Framework可以在两个场景之间添加切换过渡动画。
scene1 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene1, this);
scene2 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene2, this);
scene3 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene3, this);
scene4 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene4, this); (...) @Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
TransitionManager.go(scene1, new ChangeBounds());
break;
case R.id.button2:
TransitionManager.go(scene2, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds));
break;
case R.id.button3:
TransitionManager.go(scene3, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds_sequential));
break;
case R.id.button4:
TransitionManager.go(scene4, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds_sequential_with_interpolators));
break;
}
}
上面的代码会在同一个activity中的4个场景切换时产生过渡动画。每一次切花的动画都是不一样的。
Transition Framework会考虑当前场景内所有可见的View并计算需出需要的动画来安排两个场景之间的view的位置。
布局的改变
也可以用来给布局属性的变化加上过渡效果。你只需要做你想要的改变然后其他的就交给Transition Framework,它会为你添加动画。
a) 开启演示过渡效果
下面这行代码告诉framework我们将要对UI进行一些改变,请你给我加上过渡效果。
TransitionManager.beginDelayedTransition(sceneRoot);
b) 改变布局参数
ViewGroup.LayoutParams params = greenIconView.getLayoutParams();
params.width = 200;
greenIconView.setLayoutParams(params);
改变View的宽度属性让他变小,这会触发layoutMeasure
。这个点上,Transition framework会记录开始和结束时的相关值,并给这个变化加上过渡效果。
4. (彩蛋) 共享元素(share elements)+圆形展现(Circular Reveal)
圆形展现仅仅一个现实和隐藏一组view的动画而已。API21+可以通过ViewAnimationUtils
来使用它。
Circular Reveal动画可以结合共享元素过渡效果来创建一些有意义的动画来告诉用户app在发生中什么。
监听共享元素动画的结束
这又是怎么回事呢:
- 橙色的圆是发生在
MainActivity
和RevealActivity
之间的共享元素动画。 - 在
RevealActivity
中有一个共享元素动画结束的监听器。当动画结束时它做了两件事:- 给Toolbar执行一个Circular Reveal动画
- 使用
ViewPropertyAnimator
给RevealActivity
的其他View执行一个放大的动画
监听共享元素动画的结束
Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion);
getWindow().setSharedElementEnterTransition(transition);
transition.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionEnd(Transition transition) {
animateRevealShow(toolbar);
animateButtonsIn();
} (...) });
显示Toolbar
private void animateRevealShow(View viewRoot) {
int cx = (viewRoot.getLeft() + viewRoot.getRight()) / 2;
int cy = (viewRoot.getTop() + viewRoot.getBottom()) / 2;
int finalRadius = Math.max(viewRoot.getWidth(), viewRoot.getHeight()); Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, cx, cy, 0, finalRadius);
viewRoot.setVisibility(View.VISIBLE);
anim.setDuration(1000);
anim.setInterpolator(new AccelerateInterpolator());
anim.start();
}
放大activity的内部view
private void animateButtonsIn() {
for (int i = 0; i < bgViewGroup.getChildCount(); i++) {
View child = bgViewGroup.getChildAt(i);
child.animate()
.setStartDelay(100 + i * DELAY)
.setInterpolator(interpolator)
.alpha(1)
.scaleX(1)
.scaleY(1);
}
}
更多circular reveal动画
有很多种方式来实现一个view的显示动画,但重要的是要让这些动画有意义,它可以告诉用户app中正在发生着什么。
从目标View的中间产生一个Circular Reveal动画
reveal_green
int cx = (viewRoot.getLeft() + viewRoot.getRight()) / 2;
int cy = viewRoot.getTop();
int finalRadius = Math.max(viewRoot.getWidth(), viewRoot.getHeight()); Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, cx, cy, 0, finalRadius);
viewRoot.setBackgroundColor(color);
anim.start();
从目标View的顶部产生一个Circular Reveal动画并且结合了其他的动画
reveal_blue
int cx = (viewRoot.getLeft() + viewRoot.getRight()) / 2;
int cy = (viewRoot.getTop() + viewRoot.getBottom()) / 2;
int finalRadius = Math.max(viewRoot.getWidth(), viewRoot.getHeight()); Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, cx, cy, 0, finalRadius);
viewRoot.setBackgroundColor(color);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
animateButtonsIn();
}
});
anim.start();
从触摸点产生一个Circular Reveal动画
reveal_yellow
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
if (view.getId() == R.id.square_yellow) {
revealFromCoordinates(motionEvent.getRawX(), motionEvent.getRawY());
}
}
return false;
}
private Animator animateRevealColorFromCoordinates(int x, int y) {
float finalRadius = (float) Math.hypot(viewRoot.getWidth(), viewRoot.getHeight()); Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, x, y, 0, finalRadius);
viewRoot.setBackgroundColor(color);
anim.start();
}
动画和展现
reveal_red
Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion);
transition.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionEnd(Transition transition) {
animateRevealColor(bgViewGroup, R.color.red);
}
(...) });
TransitionManager.beginDelayedTransition(bgViewGroup, transition);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
btnRed.setLayoutParams(layoutParams);
Sample source code
https://github.com/lgvalle/Material-Animations
More information
- Alex Lockwood posts about Transition Framework. A great in deep into this topic: http://www.androiddesignpatterns.com/2014/12/activity-fragment-transitions-in-android-lollipop-part1.html
- Amazing repository with lot of Material Design samples by Saul Molinero: https://github.com/saulmm/Android-Material-Examples
- Chet Hasse video explaining Transition framework: https://www.youtube.com/watch?v=S3H7nJ4QaD8
Transition FrameWork的更多相关文章
- Animating Views Using Scenes and Transitions
From android 4.4 , it supply one new animation with layout:transition To help you animate a change b ...
- Android开发 - 掌握ConstraintLayout(十一)复杂动画!如此简单!
介绍 本系列我们已经介绍了ConstraintLayout的基本用法.学习到这里,相信你已经熟悉ConstraintLayout的基本使用了,如果你对它的用法还不了解,建议您先阅读我之前的文章. 使用 ...
- Android实习结束后的阶段性总结
2015年4月14日即将实习结束,在过去的五六个月里,对于Android开发还是学到了很多,临走前将以前做的笔记整理一下,自己也再回顾一次.零散是必然的,也可能只是一小段代码片段,但都是曾经我在学An ...
- android应用市场、社区客户端、漫画App、TensorFlow Demo、歌词显示、动画效果等源码
Android精选源码 MVP架构Android应用市场项目 android刻度盘控件源码 Android实现一个社区客户端 android商品详情页上拉查看详情 基于RxJava+Retrofit2 ...
- windows类书的学习心得
原文网址:http://www.blogjava.net/sound/archive/2008/08/21/40499.html 现在的计算机图书发展的可真快,很久没去书店,昨日去了一下,真是感叹万千 ...
- App Framework $.ui.loadContent 参数解释
在使用 app Framework 的 $.ui.loadContent(target,newTab,goBack,transition);时 对 newTab goback两个参数一直不得其解.通过 ...
- [转]Ionic – Mobile UI Framework for PhoneGap/Cordova Developers
本文转自:http://devgirl.org/2014/01/20/ionic-mobile-ui-framework-for-phonegapcordova-developers/ Ionic i ...
- EntityFramework_MVC4中EF5 新手入门教程之七 ---7.通过 Entity Framework 处理并发
在以前的两个教程你对关联数据进行了操作.本教程展示如何处理并发性.您将创建工作与各Department实体的 web 页和页,编辑和删除Department实体将处理并发错误.下面的插图显示索引和删除 ...
- [转]比较 Rational Unified Process (RUP) 和 Microsoft Solutions Framework (MSF)
文档选项 将此页作为电子邮件发送 级别: 初级 Sandra Sergi Santos, 软件工程专家, IBM 2007 年 6 月 15 日 本文来自于 Rational Edge:Micro ...
随机推荐
- Luogu 2824 [HEOI2016/TJOI2016]排序
BZOJ 4552 挺妙的解法. 听说这题直接用一个桶能拿到$80 \ pts$ 发现如果是一个排列的话,要对这个序列排序并不好做,但是假如是$01$序列的话,要对一个区间排序还是很简单的. 发现最后 ...
- iOS CocoaPods安装与使用
1.MAC安装Ruby环境 1> 安装RVM 控制台命令:$curl –L https://get.rvm.io | bash –s stable $source ~/.rvm/scripts ...
- SPOJ - BALNUM Balanced Numbers(数位dp+三进制状压)
Balanced Numbers Balanced numbers have been used by mathematicians for centuries. A positive integer ...
- sizeof的用法与字节对齐
一.sizeof是什么? sizeof是一种预编译处理,不是函数,不是一元表达式.也即,作用阶段在编译期. 二.功能是什么? sizeof返回变量或类型的字节数. 三.调用方式 sizeof(obje ...
- WPF之MVVM模式(1)
MVVM模式 一.MVVM模式概述 MVVM Pattern : Model\View\ViewModel View:视图.UI界面 ViewModel:ViewModel是对Model的封装,通过一 ...
- metasploit 读书笔记-信息收集
三、信息收集 被动信息收集 在不接触目标系统时进行的信息收集,包括使用工具Yeti、Whois (1)Whois msf > whois secmaniac.net (2)Netcraft:fi ...
- help手册使用
属性的方法名的一般规律: 设置的属性名: set+属性名 获取属性值: 1.如果是bool类型,可能是 is+属性名 或者 属性名 2.不是bool类型,就是属性名
- [SinGuLaRiTy] 2017 百度之星程序设计大赛-资格赛
[SinGuLaRiTy-1034] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 度度熊保护村庄 Time Limit: 2000/10 ...
- Python函数的冒泡排序、递归以及装饰器
函数的冒泡排序: 本质是,让元素换位置,逐个比较最终排序. 例1:实现a1,a2值互换: a1 = 123 a2 = 456 temp = a1 a1 = a2 a2 = temp print(a1) ...
- USACO 2.1.3 Sorting a Three-Valued Sequence(sort3)
这道题就是给出由123三个值的一个数字序列,然后让你把这个序列升序排序,求最小的交换次数.注意这里可以不是相邻交换. 刚开始一看题的时候,还以为t=a a=b b=t那种水题呢,然后发现不是水题.. ...