Android Transition Framework: Getting Started
In this tutorial, you’ll learn how to animate your UI with Android Transition Framework. By Zahidur Rahman Faisal.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Android Transition Framework: Getting Started
15 mins
- Switching Activities with Shared Elements
- Tracking Animation States with Lifecycle Callbacks
- Animating Transitions Between Fragments
- Creating the Image Viewer
- Sharing Transition Elements Between Fragments
- Scene Transitions
- Creating Scenes
- Switching Scenes
- Creating Custom Transitions
- Applying Custom Transitions
- Where To Go From Here?
Think about the last Android app you used. What aspect impressed you the most? Was it the user experience? All users appreciate an app that’s easy to use and blazing fast with cool animations.
With Google’s Android Transition Framework, you can add style, elegance and screen transitions to user interactions.
Android Transition Framework allows you to:
- Animate automatically from starting view to ending view by providing the layouts.
- Use predefined common animations such as Translate, Resize and Fade.
- Load built-in animations from layout resource files.
- Apply one or many animation effects to a view group at once.
- Use scenes loaded from complex layouts or generated programmatically.
- Control an animation’s lifecycle and progress providing the callbacks.
- Create custom transition animations.
- data: Model/data classes.
- ui: User interfaces related to classes.
- util: Common utility classes shared throughout the app.
Getting Started
Click the Download Materials button at the top or bottom of this tutorial. Unzip the iSellTransition.zip folder.
Now, launch Android Studio 3.3.1 or greater and select Open an existing Android Studio project to import the starter project.
Choose iSellTransition-Starter inside the iSellTransition folder and click Open.
The iSellTransition-Starter project contains the necessary classes and utilities to make an e-commerce app. It has three main packages:
Android Transition Framework offers many ways to create beautiful animations or override default ones. In the next section, you’ll create a transition and animate transitions between activities. Unleash that power now.
Creating a Transition
First, modify the default Activity Transition Animation by adding a single line of code inside SplashActivity, right before calling finish()
:
overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right)
Build and run the project.
Adding this line overrides the default enter and exit animations by referencing the existing slide-in and slide-out animations in the Android library. Easy!
Switching Activities with Shared Elements
One the most popular features of Android Transition Framework is sharing elements such as image and texts between activities during transitions.
Implement this complex animation with two simple steps:
- Update
onItemClick()
from ListActivity.kt:override fun onItemClick(item: Item, itemView: View) { val detailsIntent = Intent(this, DetailsActivity::class.java) detailsIntent.putExtra(getString(R.string.bundle_extra_item), item) // 1 - Start Activity with shared-transition animation val activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation( this@ListActivity, Pair.create<View, String>( //2 itemView.findViewById(R.id.itemImageView), // 3 getString(R.string.transition_image)), Pair.create<View, String>( itemView.findViewById(R.id.itemPrice), getString(R.string.transition_price))) startActivity(detailsIntent, activityOptions.toBundle()) }
- Open fragment_details.xml from the res ▸ layout package and add a transition name to
itemImageView
:android:transitionName="@string/transition_image"
Open content_details.xml and insert into
priceTextView
:android:transitionName="@string/transition_price"
Ready for magic? The output is awesome! Build and run again.
So smooth! Time to break it down:
- Use
ActivityOptionsCompat.makeSceneTransitionAnimation()
to specify the view and transition name as aPair
passed asBundle
from ListActivity to DetailsActivity. - Share multiple
Pair
s of View and String as arguments to that function. This is equivalent to sharingitemImageView
anditemPrice
views along with their transition names. - Specify
itemImageView
from fragment_details.xml as a final view for transition animation. Repeat the same steps forpriceTextView
in content_details.xml.
priceTextView
has a different id from the starting view itemPrice
. Don’t worry, the transition works as long as both have the same transition name and same view type, TextView.
Tracking Animation States with Lifecycle Callbacks
Now that you’ve implemented the shared element transition, you may also want to set up notification when it’s started and finished. Android Transition Framework provides a flexible API to track different animation states.
There’s a floating Share button over the product image in DetailsFragment. The button overlaps with the image transition animation. What if you could make the button visible right after the transition animation ends?
To do that, add a listener to the shared transition animation, replacing the following code at line 65 inside DetailsFragment.kt:
if (!shareFab.isShown) {
shareFab.show()
}
With:
activity?.window?.sharedElementEnterTransition?.addListener(
object : Transition.TransitionListener {
override fun onTransitionStart(transition: Transition) {}
override fun onTransitionEnd(transition: Transition) {
shareFab?.let {
if (!shareFab.isShown) {
shareFab.show()
}
}
}
override fun onTransitionCancel(transition: Transition) {}
override fun onTransitionPause(transition: Transition) {}
override fun onTransitionResume(transition: Transition) {}
})
The code above adds a TransitionListener
interface to the active window’s sharedElementEnterTransition
property, which holds the transition states.
TransitionListener
allows you to track when the transition starts, ends, cancels, pauses or resumes. You make shareFab button visible on the onTransitionEnd()
callback.
Build and run again.
Now the share button appears right after the shared transition animation.
Animating Transitions Between Fragments
Animating and sharing elements can get tricky during fragment-to-fragment transitions, but don’t worry. You can learn the trick!
Create a new Fragment called GalleryFragment, showing a complete image of the items displayed in DetailsFragment.
Creating the Image Viewer
Create fragment_gallery.xml inside the layout package. Add the code below inside the file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/details_scene_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.details.GalleryFragment">
<ImageView
android:id="@+id/itemImageView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scaleType="fitCenter"
android:transitionName="@string/transition_image" />
<TextView
android:id="@+id/titleTextView"
style="@style/TextAppearance.AppCompat.Headline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/default_padding"
android:layout_marginTop="@dimen/default_padding"
android:gravity="center"
android:text="@string/hint_title" />
</LinearLayout>
That’s a LinearLayout
to display the image and title of the selected item.
Note that you added android:transitionName="@string/transition_image"
to define a transition name for the shared element.
Now, create GalleryFragment.kt class inside the details package. Then, replace the class code with:
package com.raywenderlich.isell.ui.details
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.raywenderlich.isell.R
import com.raywenderlich.isell.data.Item
import kotlinx.android.synthetic.main.fragment_gallery.*
class GalleryFragment : Fragment() {
private var item: Item? = null
// 1
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_gallery, container, false)
}
// 2
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
item = activity?.intent?.getParcelableExtra(getString(R.string.bundle_extra_item))
item?.let {
populateDetails(item)
}
}
// 3
private fun populateDetails(item: Item?) {
itemImageView.setImageResource(item?.imageId!!)
titleTextView.text = item.title
}
}
Here’s what the code above does:
- Generate the view for this Fragment from fragment_gallery.xml.
- Retrieve
item
from DetailsActivity. CallspopulateDetails()
to populate data when the view is ready. - Assign item’s image and title to
itemImageView
andtitleTextView
.
Sharing Transition Elements Between Fragments
It’s time for a transition.
Open DetailsFragment again. Inside onViewCreated()
, after transition animation callbacks, add a click listener to itemImageView
. This will open the GalleryFragment when a user taps on it.
Add this code:
itemImageView.setOnClickListener {
//1
val changeImageAnimation = ChangeImageTransform()
//2
val galleryFragment = GalleryFragment()
galleryFragment.sharedElementEnterTransition = changeImageAnimation
galleryFragment.sharedElementReturnTransition = changeImageAnimation
//3
fragmentManager!!
.beginTransaction()
//4
.addSharedElement(itemImageView, itemImageView.transitionName)
.replace((view.parent as ViewGroup).id,
galleryFragment,
GalleryFragment::class.java.simpleName)
.addToBackStack(null)
.commit()
}
The code above performs four steps:
- Define a transition animation.
changeImageAnimation
is an instance of a library class from Android Transition Framework, which animates the shared image’s bounds from starting scene to ending scene. - Create a GalleryFragment instance and assigns
changeImageAnimation
as enter and return shared transition animation. - Navigate to GalleryFragment.
- Attach
itemImageView
as a shared element, followed by the transition name defined in the layout xml.
Build and run again.
Tap the image from detail screen to see the image animate, and bask in its glory:
Scene Transitions
Android Transition Framework provides an awesome feature called Scene Transition to switch views or layouts on the go.
You can create a Scene from a layout resource file or from a view or view group programmatically. Start by creating a scene from layouts.
Creating Scenes
Create scene_item_image.xml inside layout package and add the code below:
<?xml version="1.0" encoding="utf-8"?>
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/imageButton"
android:layout_width="match_parent"
android:layout_height="@android:dimen/thumbnail_height"
android:scaleType="fitCenter" />
Then create another file named scene_upload.xml in the same package with the following layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scene_upload"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/default_padding">
<ProgressBar
android:id="@+id/progressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<TextView
android:id="@+id/uploadStatus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/progressBar"
android:layout_margin="@dimen/default_margin"
android:gravity="center"
android:text="@string/text_uploading"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
android:textColor="@color/colorAccent" />
</RelativeLayout>
Now, replace ImageButton
inside activity_add_item.xml with FrameLayout
:
<FrameLayout
android:id="@+id/sceneContainer"
android:layout_width="match_parent"
android:layout_height="@android:dimen/thumbnail_height">
<include layout="@layout/scene_item_image" />
</FrameLayout>
In the same em>activity_add_item.xml, find the TextView
with id categoryTitle
, and replace the property android:layout_below="@+id/imageButton"
with android:layout_below="@+id/sceneContainer"
.
Similarly, find the Spinner
with id categorySpinner
and replace the property android:layout_below="@+id/imageButton"
with android:layout_below="@+id/sceneContainer"
In the above steps, you created two separate layouts (scene_item_image.xml and scene_upload.xml) and are showing one of them (scene_item_image.xml) in activity_add_item.xml. In the next section, you’ll add a listener when clicking the Add Item button inside AddItemActivity, which will trigger a scene transition from scene_item_image.xml to scene_upload.xml.
Switching Scenes
Now, open AddItemActivity and add the lines below to the import section on top:
import android.support.transition.*
import kotlinx.android.synthetic.main.scene_item_image.*
import kotlinx.android.synthetic.main.scene_upload.*
The starting scene for your transition is automatically determined from the current layout. You need to provide a target scene for your TransitionManager
to switch scenes.
To satisfy that, add the following code inside onClickAddItem()
within if (hasValidInput()) { ... }
:
fun onClickAddItem(view: View) {
if (hasValidInput()) {
// 1 - Apply Scene transition for uploading
val uploadScene: Scene = Scene.getSceneForLayout(sceneContainer, R.layout.scene_upload, this)
// 2
TransitionManager.go(uploadScene, Fade())
// ...
}
}
The code above does two things:
- Execute
Scene.getSceneForLayout()
to generate the scene and assign it touploadScene
. In order to generate a Scene, you need three parameters:- A reference to the scene root, which is
sceneContainer
. - A layout resource id, which is
R.layout.scene_upload
. - A reference to the current context.
- A reference to the scene root, which is
- Call
TransitionManager.go()
providinguploadScene
and aFade
animation to override the default transition.
Build and run to see the changes.
Navigate to add a new item. Click Add Item to see the Scene Transition in action:
Congrats, and nice work! You’ll learn to animate the uploading process in the next part of this tutorial.
Creating Custom Transitions
You can create you own transition animation, simulating an upload process in AddItemActivity.
To create a transition, you need to extend Transition
class from Android Transition Framework and manipulate your animation.
To do that, you’ll customize the default progress animation of ProgressBar
.
Create a new class named ProgressBarTransition inside the util package and add following code:
package com.raywenderlich.isell.util
import android.widget.ProgressBar
import android.view.ViewGroup
import android.animation.Animator
import android.animation.ObjectAnimator
import android.view.animation.DecelerateInterpolator
import android.support.transition.Transition
import android.support.transition.TransitionValues
class ProgressBarTransition : Transition() {
val PROGRESSBAR_PROPERTY = "progress"
val TRANSITION_PROPERTY = "ProgressBarTransition:progress"
// 1
override fun createAnimator(sceneRoot: ViewGroup, startValues: TransitionValues?,
endValues: TransitionValues?): Animator? {
if (startValues != null && endValues != null && endValues.view is ProgressBar) {
val progressBar = endValues.view as ProgressBar
val startValue = startValues.values[TRANSITION_PROPERTY] as Int
val endValue = endValues.values[TRANSITION_PROPERTY] as Int
if (startValue != endValue) {
// 2
val objectAnimator = ObjectAnimator
.ofInt(progressBar, PROGRESSBAR_PROPERTY, startValue, endValue)
objectAnimator.interpolator = DecelerateInterpolator()
return objectAnimator
}
}
return null
}
// 3
private fun captureValues(transitionValues: TransitionValues) {
if (transitionValues.view is ProgressBar) {
// Save current progress in the transitionValues Map
val progressBar = transitionValues.view as ProgressBar
transitionValues.values[TRANSITION_PROPERTY] = progressBar.progress
}
}
// 4
override fun captureStartValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
// 5
override fun captureEndValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
}
In the first part of the code above, the important arguments are startValues
and endValues
as TransitionValues. A TransitionValues instance holds information about the View and a Map with properties and current values from the view.
The arguments startValues
and endValues
contain the value for a property name modified in this transition. If there are startValues
and endValues
and the view type is ProgressBar, then the code extracts the values and puts them into startValue
and endValue
variables, respectively.
Here’s what the remaining code does:
- Creates an ObjectAnimator to animate changes of the progress property from ProgressBar. Set a DecelerateInterpolator to reflect the hustle of uploading data during the progress animation.
- Takes the progress value of the progress bar and save it to TransitionValues instance internal Map along with
TRANSITION_PROPERTY
. - Stores the starting state using
captureStartValues()
. - Stores the end state of the transition using
captureEndValues()
.
Applying Custom Transitions
Now, you’ll apply your custom transition while simulating the upload process. Open AddItemActivity and import ProgressBarTransition:
import com.raywenderlich.isell.util.ProgressBarTransition
Then, replace everything inside onClickAddItem()
:
fun onClickAddItem(view: View) {
if (hasValidInput()) {
// Scene Transition
val uploadScene: Scene = Scene
.getSceneForLayout(sceneContainer, R.layout.scene_upload, this)
TransitionManager.go(uploadScene, Fade())
// 1
val uploadTime: Long = 3000
val statusInterval: Long = 600
object : CountDownTimer(uploadTime, statusInterval) {
val maxProgress = 100
var uploadProgress = 0
// 2
override fun onTick(millisUntilFinished: Long) {
if (uploadProgress < maxProgress) {
uploadProgress += 20
TransitionManager
.beginDelayedTransition(sceneContainer, ProgressBarTransition())
progressBar.progress = uploadProgress
}
}
// 3
override fun onFinish() {
uploadStatus.text = getString(R.string.text_uploaded)
addItemData()
showAddItemConfirmation()
}
}.start()
}
}
Here’s what’s happening in the code above:
- Declare
uploadTime
for three seconds andstatusInterval
for 600 milliseconds. CountDownTimer will fire itsonTick()
function five times before going toonFinish()
. -
onTick()
checks whetheruploadProgress
reached 100%. If not, it increasesuploadProgress
by 20% and callsTransitionManager.beginDelayedTransition()
to apply your customProgressBarTransition
onsceneContainer
. -
onFinish()
fires after three seconds. ChangeuploadStatus
text to Upload complete! and then add a confirmation message by callingaddItemData()
andshowAddItemConfirmation()
.
Build and run now, and try adding another item:
Congratulations! You've mastered the art of transitions and turned a simple app into an awesome one!
Where To Go From Here?
Get the final project by clicking the Download Materials button at the top or bottom of this tutorial.
Keep your users excited with the magic of Android animations. Here are some additional resources to explore:
- Deep-diving Android Animations
- Mastering Property Animation
- Getting started with Android MotionLayout
- Getting started with Android Design Support Library
I hope learning Android Transition Framework helps you create amazing user experiences for your next app. If you have questions or comments, please join the forum discussion and comment below!
All videos. All books.
One low price.
A Kodeco subscription is the best way to learn and master mobile development — plans start at just $19.99/month! Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.
Learn more