切换Activity的Transition动画:
需要开启如下属性:
<item name="android:windowContentTransitions">true</item>
如果需要设置动画切换时是否允许覆盖,即在前一个完全动画消失,后一个可以开始动画出现,即两个Activity的动画顺序关系,如果为false,则是前一个动画完全消失后,后一个才开始动画出现;为true时,则两个动画几乎同时开始:
<item name="android:windowAllowEnterTransitionOverlap">false</item>
<item name="android:windowAllowReturnTransitionOverlap">false</item>
本Activity设置的Transition动画,只应用在本Activity,与切换中的另一个Activity无关。Transition除了选择样式外,还需为该Activity的不同场景设置Transition。
Transition在窗口中的应用场景
ReenterTransition(Activity从stop中resume时)、ExitTransition(Activity切换中被stop时使用)、EnterTranstion(窗口启动时)、ReturnTransition(窗口关闭时)。
Transition有哪些样式:https://developer.android.com/reference/android/transition/Transition.html
根据以上关系可以看出,可以实现Transition或它的子类可以实现定义Transition动画样式。
Transition的XML属性
XML attributes |
|
---|---|
android:duration |
Amount of time (in milliseconds) that the transition should run. |
android:interpolator |
Interpolator to be used in the animations spawned by this transition. |
android:startDelay |
Delay in milliseconds before the transition starts. |
Fade动画非继承的XML属性:
XML attributes |
|
---|---|
android:slideEdge |
动画可以在XML文件中定义,也可以在代码中创建。放在XML文件需要放在res/transition/下。
Transition的xml格式如下:
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<explode/>
<changeBounds/>
<changeTransform/>
<changeClipBounds/>
<changeImageTransform/>
</transitionSet>
也可单独一个元素:
<changeBounds/>
使用自定义Transition时的格式:
<transition class="my.app.transition.CustomTransition"/>
explode.xml:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<explode
android:duration="@integer/anim_duration_long"
android:interpolator="@android:interpolator/bounce" />
</transitionSet>
slide.xml:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<slide
android:duration="@integer/anim_duration_long"
android:slideEdge="bottom" />
</transitionSet>
1.设置slide动画:
private void setupWindowAnimations() {
// Re-enter transition is executed when returning to this activity
Slide slideTransition = new Slide();
slideTransition.setSlideEdge(Gravity.LEFT);
slideTransition.setDuration(getResources().getInteger(R.integer.anim_duration_long));
getWindow().setReenterTransition(slideTransition);
getWindow().setExitTransition(slideTransition);
}
使用Scene:
1.从当前显示的Layout中选择一个ViewGroup作为SceneRoot。
2.准备好一个Layout,使用Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene0, context);获取一个Scene对象scene0
3.TransitionManager.go(scene0)或者TransitionManager.go(scene0,transition);
代码片段示例:
private void setupLayout() {
final ViewGroup activityRoot = (ViewGroup) findViewById(R.id.buttons_group);
ViewGroup sceneRoot = (ViewGroup) findViewById(R.id.scene_root);
scene0 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene0, this);
scene0.setEnterAction(new Runnable() {
@Override
public void run() {
for (int i = 0; i < viewsToAnimate.size(); i++) {
View child = viewsToAnimate.get(i);
child.animate()
.setStartDelay(i * DELAY)
.scaleX(1)
.scaleY(1);
}
}
});
scene0.setExitAction(new Runnable() {
@Override
public void run() {
TransitionManager.beginDelayedTransition(activityRoot);
View title = scene0.getSceneRoot().findViewById(R.id.scene0_title);
title.setScaleX(0);
title.setScaleY(0);
}
});
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);
View button1 = findViewById(R.id.sample3_button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TransitionManager.go(scene1, new ChangeBounds());
// TransitionManager.go(scene1);
}
});
View button2 = findViewById(R.id.sample3_button2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TransitionManager.go(scene2, TransitionInflater.from(AnimationsActivity2.this).
inflateTransition(R.transition.slide_and_changebounds));
}
});
View button3 = findViewById(R.id.sample3_button3);
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TransitionManager.go(scene3, TransitionInflater.from(AnimationsActivity2.this).
inflateTransition(R.transition.slide_and_changebounds_sequential));
}
});
View button4 = findViewById(R.id.sample3_button4);
button4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TransitionManager.go(scene4, TransitionInflater.from(AnimationsActivity2.this).
inflateTransition(R.transition.slide_and_changebounds_sequential_with_interpolators));
}
});
viewsToAnimate.add(button1);
viewsToAnimate.add(button2);
viewsToAnimate.add(button3);
viewsToAnimate.add(button4);
}
其中sceneRoot是一个FrameLayout,当Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene0, this)时,sceneRoot中所有Child都会被移除,R.layout.activity_animations_scene0会add到sceneRoot中,作为sceneRoot的childView。当Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene1, this)时,会把R.layout.activity_animations_scene0这个View从sceneRoot中移走,然后activity_animations_scene1添加到sceneRoot中。
Transition可以是TransitionSet,而其中各个动画可以是并行执行的,也可以是串行执行的,默认是并行的,如果要串行的话,如下定义xml:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/anim_duration_long"
android:transitionOrdering="sequential">
<slide />
<changeBounds />
</transitionSet>
Transition中的Slide、Fade和属性动画中translateX/Y、alpha类似,但是更加复杂,所以效果表现更佳。而相对来说explode、changeBounds、ChangeImageTransform就更加复杂了。explode动画效果是root中的childView向某个中心点合过来或者远离中心点。changeBounds则是会旋转移动,但是前后两个Scene的Layout中的view的id要有相同的才有效果,如下两个Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/square_green"
style="@style/MaterialAnimations.Icon.Big"
android:src="@drawable/circle_24dp"
android:layout_alignParentRight="true"
android:tint="@color/sample_green" />
<ImageView
android:id="@+id/square_blue"
style="@style/MaterialAnimations.Icon.Big"
android:src="@drawable/circle_24dp"
android:tint="@color/sample_blue" />
<ImageView
android:id="@+id/square_red"
style="@style/MaterialAnimations.Icon.Big"
android:layout_below="@+id/square_green"
android:layout_alignParentRight="true"
android:src="@drawable/circle_24dp"
android:tint="@color/sample_red" />
<ImageView
android:id="@+id/square_yellow"
style="@style/MaterialAnimations.Icon.Big"
android:layout_below="@+id/square_blue"
android:src="@drawable/circle_24dp"
android:tint="@color/sample_yellow" />
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/square_green"
style="@style/MaterialAnimations.Icon.Big"
android:layout_below="@id/square_blue"
android:layout_alignParentRight="true"
android:src="@drawable/circle_24dp"
android:tint="@color/sample_green" />
<ImageView
android:id="@+id/square_blue"
style="@style/MaterialAnimations.Icon.Big"
android:layout_alignParentRight="true"
android:src="@drawable/circle_24dp"
android:tint="@color/sample_blue" />
<ImageView
android:id="@+id/square_red"
style="@style/MaterialAnimations.Icon.Big"
android:layout_below="@+id/square_yellow"
android:src="@drawable/circle_24dp"
android:tint="@color/sample_red" />
<ImageView
android:id="@+id/square_yellow"
style="@style/MaterialAnimations.Icon.Big"
android:src="@drawable/circle_24dp"
android:tint="@color/sample_yellow" />
</RelativeLayout>
public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition)
调用上面的方法后,sceneRoot代表的控件树发生任何显示上的变化,都会应用transition动画去展示,即本帧与下一帧之间使用过渡动画;比如执行以下代码:
private void changeLayout() {
TransitionManager.beginDelayedTransition(viewRoot);
ViewGroup.LayoutParams params = square.getLayoutParams();
if (sizeChanged) {
params.width = savedWidth;
} else {
savedWidth = params.width;
params.width = 200;
}
sizeChanged = !sizeChanged;
square.setLayoutParams(params);
}
会导致重新布局并重绘,那么就会产生transition动画。又比如以下PopupWindow的代码:
private void startEnterTransition(Transition enterTransition) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
enterTransition.addTarget(child);
child.setVisibility(View.INVISIBLE);
}
TransitionManager.beginDelayedTransition(this, enterTransition);
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
child.setVisibility(View.VISIBLE);
}
}
会产生一个动画,因为View从不可见到可见了。那前后两帧不同了,这个两帧使用什么动画去过渡,就看传入的transition参数了。
使用Transition.addTarget(View)选择指定的childView变化才使用过渡动画:
Transition transiton = new Explode();
//linearLayout包括了两个button,linearLayout visibility变化时指定只对bt1做explode动画
linearLayout.setVisibility(View.INVISIBLE);
transiton.addTarget(bt1);
transiton.setDuration(1000);
TransitionManager.beginDelayedTransition(l,transiton);
linearLayout.setVisibility(View.VISIBLE);
在通过WindowManager.addView添加窗口时,可以使用以下代码模拟进入窗口的动画:
public void requestEnterTransition(Transition transition) {
final ViewTreeObserver observer = getViewTreeObserver();
if (observer != null && transition != null) {
final Transition enterTransition = transition.clone();
// Postpone the enter transition after the first layout pass.
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
final ViewTreeObserver observer = getViewTreeObserver();
if (observer != null) {
observer.removeOnGlobalLayoutListener(this);
}
final Rect epicenter = getTransitionEpicenter();
enterTransition.setEpicenterCallback(new EpicenterCallback() {
@Override
public Rect onGetEpicenter(Transition transition) {
return epicenter;
}
});
startEnterTransition(enterTransition);
}
});
}
}
private void startEnterTransition(Transition enterTransition) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
enterTransition.addTarget(child);
child.setVisibility(View.INVISIBLE);
}
TransitionManager.beginDelayedTransition(this, enterTransition);
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
child.setVisibility(View.VISIBLE);
}
}
Shared Element Transition
1.在两边Activity的layout中都通过android:transitionName或者通过代码View.setTransitionName设置sharedElement的共同name。如果时Activity切换,那么待消失那边的layout可以不设置transitionName,因为ActivityOption中已经指定了哪个对象作为共享元素。
2.创建一个ActivityOption:
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
Pair.create(view1, "agreedName1"),
Pair.create(view2, "agreedName2"));
3.使用上面的ActivityOption启动Activity:
Activity.startActivity(intent, options.toBundle())
使用切换Frament时也可以使用SharedElement:
1.在两边Fragment的layout中都通过android:transitionName或者通过代码View.setTransitionName设置sharedElement的共同name
2.
getFragmentManager().beginTransaction()
.replace(R.id.viewgroup, fragment)
.addToBackStack(null)
.addSharedElement(view, getString(R.string.view_id_name))
.commit();
上面的view是待消失的fragment中的view对象,view_id_name是待显示的fragment的view的transitionName。
设定SharedElement使用哪种动画,如果没有设置,则是默认的Transition:
Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion);
getWindow().setSharedElementEnterTransition(transition);
transition.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionStart(Transition transition) {
}
@Override
public void onTransitionEnd(Transition transition) {
// Removing listener here is very important because shared element transition is executed again backwards on exit. If we don't remove the listener this code will be triggered again.
transition.removeListener(this);
hideTarget();
animateRevealShow(toolbar);
animateButtonsIn();
}
@Override
public void onTransitionCancel(Transition transition) {
}
@Override
public void onTransitionPause(Transition transition) {
}
@Override
public void onTransitionResume(Transition transition) {
}
});
只要设置了Transition,无论是从xml设置的还是从代码中设置的,都可以通过getWindow().getxxxTransition()方法设置监听器或者其他东西
Reveal Transition
//viewRoot是需要应用暴露动画的View,x,y为暴露中心,暴露开始半径,暴露终点半径
Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, x, y, 0, finalRadius);
viewRoot.setBackgroundColor(ContextCompat.getColor(this, color));
anim.setDuration(getResources().getInteger(R.integer.anim_duration_long));
anim.setInterpolator(new AccelerateDecelerateInterpolator());
anim.start();
让各个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);
}
}
private void animateButtonsOut() {
for (int i = 0; i < bgViewGroup.getChildCount(); i++) {
View child = bgViewGroup.getChildAt(i);
child.animate()
.setStartDelay(100 + i * DELAY)
.setInterpolator(interpolator)
.alpha(0)
.scaleX(0f)
.scaleY(0f);
}
}
监听动画各个生命周期
监听Scene:
scene0 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene0, this);
scene0.setEnterAction(new Runnable() {
@Override
public void run() {
//在进入该Scene前
}
}
});
scene0.setExitAction(new Runnable() {
@Override
public void run() {
在退出该Scene前
}
});
监听Transition:
transition.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionStart(Transition transition) {
}
@Override
public void onTransitionEnd(Transition transition) {
}
@Override
public void onTransitionCancel(Transition transition) {
}
@Override
public void onTransitionPause(Transition transition) {
}
@Override
public void onTransitionResume(Transition transition) {
}
});
参考自:https://github.com/lgvalle/Material-Animations/(这个是一个很的全面Transition Demo)
https://github.com/googlesamples/android-ActivitySceneTransitionBasic