No maintainance is intended. The content is still valid as a reference but it won't contain the latest new stuff
[Android Transition Framework][transition-framework] can be used for three main things:
Animate existing activity layout content
![A Start B][transition_a_to_b]
When transitioning from Activity A to Activity B content layout is animated according to defined transition. There are three predefined transitions available on android.transition.Transition you can use: Explode, Slide and Fade.
All these transitions track changes to the visibility of target views in activity layout and animate those views to follow transition rules.
| [Explode][explode_link] | [Slide][slide_link] | [Fade][fade_link] |
|---|---|---|
| ![transition_explode] | ![transition_slide] | ![transition_fade] |
You can define these transitions declarative using XML or programmatically. For the Fade Transition sample, it would look like this:
Transitions are defined on XML files in 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"/>
To use these transitions you need to inflate them using 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);
}
![transition_fade]
Activity A starts Activity B
Transition Framework finds A Exit Transition (slide) and apply it to all visible views.
returnTransition and reenterTransition, these have been executed instead) Return and Reenter Transitions are the reverse animations for Enter and Exit respectively.
If Return or Reenter are not defined, Android will execute a reversed version of Enter and Exit Transitions. But if you do define them, you can have different transitions for entering and exiting an activity.
![b back a][transition_b_to_a]
We can modify previous Fade sample and define a ReturnTransition for TransitionActivity, in this case, a Slide transition. This way, when returning from B to A, instead of seeing a Fade out (reversed Enter Transition) we will see a Slide out transition
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);
}
Observe that if no Return Transition is defined then a reversed Enter Transition is executed. If a Return Transition is defined that one is executed instead.
| Without Return Transition | With Return Transition |
|---|---|
Enter: Fade In |
Enter: Fade In |
Exit: Fade Out |
Exit: Slide out |
| ![transition_fade] | ![transition_fade2] |
The idea behind this is having two different views in two different layouts and link them somehow with an animation.
Transition framework will then do whatever animations it consider necessary to show the user a transition from one view to another.
Keep this always in mind: the view is not really moving from one layout to another. They are two independent views.
![A Start B with shared][shared_element]
This is something you need to set up once on your app styles.xml.
values/styles.xml
<style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar">
...
<item name="android:windowContentTransitions">true</item
...
</style>
Here you can also specify default enter, exit and shared element transitions for the whole app if you want
<style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar">
...
<item name="android:windowEnterTransition">@transition/explode</item>
<item name="android:windowExitTransition">@transition/explode</item>
<item name="android:windowSharedElementEnterTransition">@transition/changebounds</item>
<item name="android:windowSharedElementExitTransition">@transition/changebounds</item>
...
</style>
To make the trick you need to give both, origin and target views, the same android:transitionName. They may have different ids or properties, but android:transitionName must be the same.
layout/activity_a.xml
<ImageView
android:id="@+id/small_blue_icon"
style="@style/MaterialAnimations.Icon.Small"
android:src="https://github.com/lgvalle/Material-Animations/raw/main/@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="https://github.com/lgvalle/Material-Animations/raw/main/@drawable/circle"
android:transitionName="@string/blue_name" />
Use the ActivityOptions.makeSceneTransitionAnimation() method to define shared element origin view and transition name.
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());
}
});
Just that code will produce this beautiful transition animation:
![a to b with shared element][shared_element_anim]
As you can see, Transition framework is creating and executing an animation to create the illusion that views are moving and changing shape from one activity to the other
Shared element transition works with Fragments in a very similar way as it does with activities.
Steps a) and b) are exactly the same. Only c) changes
values/styles.xml
<style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar">
...
<item name="android:windowContentTransitions">true</item>
...
</style>
layout/fragment_a.xml
<ImageView
android:id="@+id/small_blue_icon"
style="@style/MaterialAnimations.Icon.Small"
android:src="https://github.com/lgvalle/Material-Animations/raw/main/@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="https://github.com/lgvalle/Material-Animations/raw/main/@drawable/circle"
android:transitionName="@string/blue_name" />
To do this you need to include shared element transition information as part of the FragmentTransaction process.
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();
And this is the final result:
![shared_element_no_overlap]
You can define if enter and exit transitions can overlap each other.
From Android documentation:
When true, the enter transition will start as soon as possible.
When false, the enter transition will wait until the exit transition completes before starting.
This works for both Fragments and Activities shared element transitions.
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();
It is very easy to spot the difference in this example:
| Overlap True | Overlap False |
|---|---|
| Fragment_2 appears on top of Fragment_1 | Fragment_2 waits until Fragment_1 is gone |
| ![shared_element_overlap] | ![shared_element_no_overlap] |
Transition Framework can also be used to animate element changes within current activity layout.
Transitions happen between scenes. A scene is just a regular layout which defines a static state of our UI. You can transition from one scene to another and Transition Framework will animate views in between.
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;
}
}
That code would produce transition between four scenes in the same activity. Each transition has a different animation defined.
Transition Framework will take all visible views in current scene and calculate whatever necessary a
$ claude mcp add Material-Animations \
-- python -m otcore.mcp_server <graph>