android.Transition Framework can be used for three main things:
- Animate View elements in transitions between activites (or fragments)
- Animate shared elements (hero views) in transitions between activities (or fragments)
- Animate View elements from one activity scene to another.
Animate existing activity layout content (non-hero views)
You can define these transitions declarative using XML or programatically.
res/transition/activity_explode.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<explode android:duration="2000"/>
</transitionSet>
res/values/style.xml
<item name="android:windowEnterTransition">@transition/activity_explode.xml</item>
To inflate specific xml defined transition:
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupWindowAnimations();
}
private void setupWindowAnimations() {
Explode explode = TransitionInflater.from(this).inflateTransition(R.transition.activity_explode);
explode.setDuration(2000);
getWindow().setExitTransition(explode);
}
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupWindowAnimations();
}
private void setupWindowAnimations() {
Explode explode = new Explode();
explode.setDuration(2000);
getWindow().setExitTransition(explode);
}
DetailActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupWindowAnimations();
}
private void setupWindowAnimations() {
Explode explode = new Explode();
explode.setDuration(2000);
getWindow().setEnterTransition(explode);
}
![A start B exmaple] (https://raw.githubusercontent.com/lgvalle/Material-Animations/master/screenshots/example1.gif)
-
Activity A starts Activity B
-
Transition Framework finds A Exit Transition (explode) and apply it to all visible views.
-
Transition Framework finds B Enter Transition (explode) and apply it to all visible views.
-
On Back Pressed Transition Framework executes Enter and Exit reverse animations respectively (because it cannot find
returnTransition
andreenterTransition
)
These two methods define the reverse animations for enter
and exit
respectively.
In our example, if we do:
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupWindowAnimations();
}
private void setupWindowAnimations() {
Explode explode = new Explode();
explode.setDuration(2000);
getWindow().setExitTransition(explode);
Fade fade = new Fade();
fade.setDuration(2000);
getWindow().setReenterTransition(fade);
}
DetailActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupWindowAnimations();
}
private void setupWindowAnimations() {
Explode explode = new Explode();
expl.setDuration(2000);
getWindow().setEnterTransition(explode);
Fade fade = new Fade();
fade.setDuration(2000);
getWindow().setReturnTransition(fade);
}
We have a nice Explode for going forward and Fade for backward:
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.
As you can see there are two views with ids 'smallSquare' and 'bigSquare'. But they have the same 'transitionName'.
This way the Transition Framework knows it needs to create an animation from one view to the other.
MainActivity.java
squareBlue.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, DetailActivity2.class);
View sharedView = squareBlue;
String transitionName = getString(R.string.square_blue_name);
ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, sharedView, transitionName);
startActivity(i, transitionActivityOptions.toBundle());
}
});
layout/main_activity.xml
<View
android:layout_margin="10dp"
android:id="@+id/square_blue"
android:layout_width="50dp"
android:background="@android:color/holo_blue_light"
android:transitionName="@string/square_blue_name"
android:layout_height="50dp"/>
layou/details_activity2.xml
<View
android:layout_width="150dp"
android:id="@+id/big_square_blue"
android:layout_margin="10dp"
android:transitionName="@string/square_blue_name"
android:layout_centerInParent="true"
android:background="@android:color/holo_blue_light"
android:layout_height="150dp" />
Just that code will produce this beautiful transition animation:
As you can see, Transition framework is creating and executing an animation to create the illusion that the view is moving and changing shape.
To proof the blue square view is not really moving we can do this quick exercise: change transitioName in DetailsActivity from Big Blue Square to the Title Text above it.
<TextView
android:layout_width="wrap_content"
android:text="Activity Detail 2"
style="@style/Base.TextAppearance.AppCompat.Large"
android:layout_centerHorizontal="true"
android:transitionName="@string/square_blue_name"
android:layout_above="@+id/big_square_blue"
android:layout_height="wrap_content" />
If we now execute the app we have the same behaviour but targeting a different view:
Transition framework can also be used to animate element changes within current activity layout.
Transitions happen between scenes. An scene defines a static state of our UI. You can do complex things regarding scenes but I want to keep this example as simple as possible.
If you want to know more about scenes I recomend you check [this video by Chet Hasse] (https://www.youtube.com/watch?v=S3H7nJ4QaD8)
In this example I'm going to use the easier way to animate layout changes inside an Activity layout:
TransitionManager.beginDelayedTransition(sceneRoot);
With just this line of code we are telling the framework we are going to perform some UI changes that it will need to animate.
After that we made the changes on our UI elements:
setViewWidth(squareRed, 500);
setViewWidth(squareBlue, 500);
setViewWidth(squareGreen, 500);
setViewWidth(squareYellow, 500);
This will change those views width attribute to make it larger. That will trigger a layoutMeasure
. At that point the Transition framework will record start and ending values and will create an animation to transition from one to another.
squareGreen.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TransitionManager.beginDelayedTransition(sceneRoot);
setViewWidth(squareRed, 500);
setViewWidth(squareBlue, 500);
setViewWidth(squareGreen, 500);
setViewWidth(squareYellow, 500);
}
});
}
private void setViewWidth(View view, int x) {
ViewGroup.LayoutParams params = view.getLayoutParams();
params.width = x;
view.setLayoutParams(params);
}
Circular Reveal is just an animation to show or hide a group of UI elements. It is available since API 21 in ViewAnimationUtils
class.
In this example I'm going to demostrate how can you make use of Shared Element Transition and Circular Reveal Animation to smoothly switch UI context.
What is happening step by step is:
- Shared orange box is transitioning from
MainActivity
toDetailsActivity
. DetailsActivity
background viewgroup visibility starts asINVISIBLE
.
<RelativeLayout
android:layout_width="match_parent"
android:id="@+id/backgroundViewGroup"
android:visibility="invisible"
...
- After
SharedElementEnterTransition
ends aCircularReveal
animation takes place making the background viewgroup visible.
Transition enterTransition = getWindow().getSharedElementEnterTransition();
enterTransition.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionStart(Transition transition) {}
@Override
public void onTransitionEnd(Transition transition) {
animateRevealShow(bgViewGroup);
}
@Override
public void onTransitionCancel(Transition transition) {}
@Override
public void onTransitionPause(Transition transition) {}
@Override
public void onTransitionResume(Transition transition) {}
});
On exit transition steps are:
SharedElementReturnTransition
is delayed 1 second.
Transition sharedElementReturnTransition = getWindow().getSharedElementReturnTransition();
sharedElementReturnTransition.setStartDelay(ANIM_DURATION);
ReturnTransition
duration is setted to 1 second. Have in mind this are two different transitions.
Transition returnTransition = getWindow().getReturnTransition();
returnTransition.setDuration(ANIM_DURATION);
- On
ReturnTransition
start aCircularReveal
animation takes place hiding the background viewgroup.
returnTransition.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionStart(Transition transition) {
animateRevealHide(bgViewGroup);
}
@Override
public void onTransitionEnd(Transition transition) {}
@Override
public void onTransitionCancel(Transition transition) {}
@Override
public void onTransitionPause(Transition transition) {}
@Override
public void onTransitionResume(Transition transition) {}
});
- After 1 second,
CircularReveal
has finished andSharedElementReturnTransition
gets executed producing orange box animation.
- Alex Lockwood posts about transitions in Lollipop. A great in deep into this topic: http://www.androiddesignpatterns.com/2014/12/activity-fragment-transitions-in-android-lollipop-part1.html
- Very complete repository with examples by Saul Molinero: https://github.com/saulmm/Android-Material-Examples
- Chet Hasse video explaining Transition framework: https://www.youtube.com/watch?v=S3H7nJ4QaD8
The MIT License (MIT)
Copyright (c) 2015 Luis G. Valle
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.