Hello!
On Habré, the
Transitions API theme for animations that appeared in Android since 4.4 (KitKat) and continued its development in 5.0 (Lollipop) has not yet been covered. In my article, I will talk about how to simplify work with animations using them and how to apply them on any device with
Android version
4.0 and higher .

Together with Android 4.4, a new mechanism for animating changes to the layout was introduced. Even in version 4.0, the first solution to this problem appeared - the animateLayoutChange flag for ViewGroup. But even with calling the getLayoutTransition () method and changing its parameters, it was not flexible enough and did not give full control over how our change (transition) would be animated.
')
KitKat Transition API brings us the concept of the scene -
Scene and some changes between scenes -
Transition . In addition to them, we introduce the concept of
Scene root to define the root layout, within which the scene changes will occur. And the scene itself is a certain wrapper over the ViewGroup, which describes the specific state of him and all the View objects contained in it.
Now about
Transition itself. This is the mechanism responsible for reading the required values of the parameters of the View, which have changed when changing scenes and generating animations for smoothly changing these states.
Let's start with the simplest use case for the Transitions API.
Imagine that the current state of our layout is the first scene. Suppose we just have a square.
We describe layout in xml:
<FrameLayout android:id="@+id/container" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <View android:id="@+id/transition_square" android:layout_width="@dimen/square_size_normal" android:layout_height="@dimen/square_size_normal" android:background="@color/square_bg"/> </FrameLayout>
Now we just want to animate the size of our square. To begin to get used to the concept of scenes we call this change the transition to the second scene.
ViewGroup sceneRoot = (ViewGroup) findViewById(R.id.container); View square = mSceneRoot.findViewById(R.id.transition_square); int newSquareSize = getResources().getDimensionPixelSize(R.dimen.square_size_expanded);
Result:

Not bad, the animation is just one line. Moreover, such changes inside the layout can be any number.
Now we will try to set up some animation parameters. For this you need to specify a specific Transition, which we will perform. The
beginDelayedTransition method can take the second parameter of any heir of the Transition class. It is about them, we now talk.
Simple Transition Types
- ChangeBounds . That it was performed in the first example. This is the Transition, which is responsible for changing the coordinates of the View inside the layout and its size.
- Fade . It combines the well-known animations fade in and fade out. It is an inheritor of the Visibility class, which was invented for those Transitions that are performed at the moment when the View was visible in the first scene, but should disappear when it passes to the second, or, conversely, appear.
- TransitionSet . It is also the heir of Transition, but it is just a collection of some number of other Transitions that are performed in turn or simultaneously. The sequence is set using the setOrdering method.
- AutoTransition . It is such a TransitionSet, which is merely a set of sequentially running Fade with the Fade.OUT, ChangeBounds, and Fade parameter with the Fade.IN parameter. Thus, first with the fade out effect all views hide, which are not in the second scene, then move and resize for the View, in which these parameters have changed, and, finally, new elements from the second scene appear with the fade in. It is AutoTransition that is executed when, as in the first example, we did not specify any specific Transition method to beginDelayedTransition.
Scenes
Let's go back to the scenes. In the first example, we sorted out the easiest way to use the API. But scenes can be described more formally, for example, to make it easier to switch between them.
We describe the layout of our Activity so that it has a button for changing scenes and FrameLayout, which will be our Scene root. Inside it we immediately put the layout, which is the first scene, but we will put it into a separate file.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <Button android:id="@+id/btn_change_scene" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Change scene" android:layout_gravity="center_horizontal" /> <FrameLayout android:id="@+id/scene_root" android:layout_width="match_parent" android:layout_height="match_parent"> <include layout="@layout/scene1"/> </FrameLayout> </LinearLayout>
The contents of scene1. Place there all the same square, but in the upper left corner:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout android:id="@+id/container" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <View android:id="@+id/transition_square" android:layout_width="@dimen/square_size_normal" android:layout_height="@dimen/square_size_normal" android:background="@color/square_bg" android:layout_gravity="top|left"/> </FrameLayout>
And create a scene2 file. Move the square to the upper right corner and increase in size. Also, add a TextView, which was not in the first scene.
<FrameLayout android:id="@+id/container" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <View android:id="@+id/transition_square" android:layout_width="@dimen/square_size_expanded" android:layout_height="@dimen/square_size_expanded" android:background="@color/square_bg" android:layout_gravity="top|right"/> <TextView android:id="@+id/transition_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="This text fades in." android:textAppearance="?android:attr/textAppearanceLarge"/>/> </FrameLayout>
In the onCreate Activity we describe:
ViewGroup sceneRoot = (ViewGroup) findViewById(R.id.scene_root);
Result:

And about the main thing. Backport
It is easy to understand why few more people use this API, and even very few people know about it. Now is the time when developers have only recently realized the moral right to abandon support for Android 2.x and most new projects finally start with minSdk support for Android 4.0. But for the time being, there is little point in using an API that will only work starting from 4.4, because most devices are still on older versions.
But! Transition API can already be used for Android 4.0+ and everywhere the animation will work the same. For this, I am developing a library called
Transitions Everywhere . It is the backport of the Transition API to older versions of Android. To integrate into your project, you need to connect the library as a gradle dependency:
dependencies { compile "com.andkulikov:transitionseverywhere:1.7.4" }
And for all classes associated with this API, replace the import with the
com.transitionseverywhere. * Package instead of
android.transition. *About the library
When I became interested in this topic I found two similar libraries:
github.com/guerwan/TransitionsBackportgithub.com/pardom/TransitionSupportLibraryWhat unites them is the fact that their creators apparently scored on their development and both of them did not fully implement everything that could be implemented for backward compatibility of some functionalities on older versions of Android. I took them as a basis and added a lot of new things. Various fixes of some incorrect behaviors that manifested themselves, for example, in versions prior to 4.3. In addition, the API of my library is compatible with versions of Android 2.2+, but on versions up to 4.0, a change of scenes (layout) will be performed without animation. Also, I try to keep up with the times. Not so long ago, a new version of Android 5.0 (Lollipop) was released, and the code for the entire transitions API package in it has also changed a lot. I have made all the changes to my library.
What's new in the API of 5.0
- Now you can set TransitionName for View. Marking the same View name in the first and second scenes, we say that the transition between them needs to be animated. This is convenient, because earlier we needed for two scenes described in different xml files to specify the same id for such a View. In 5.0, transitionName can be specified as an attribute in the description via xml, or by calling the setTransitionName method on any heir of the View. In my library, I partially ported this functionality in the following way: I suggest calling the static method TransitionManager.setTransitionName (View v, String transitionName) instead of the similar one from View's heir and abandoning the transitionName setting via xml.
- Slide . New Transition, which is the heir of Visibility, as well as Fade. Using it, appearing in the scene View can “resort” from the selected edge. Example with new Slide (Gravity.LEFT)):

- Explode . In many ways similar to Slide, but View will run out either from a certain direction, which is set using the so-called Transition epicenter (see the setEpicenterCallback method) or from a random angle, if this epicenter is not specified.
- TransitionPropagation. Describes the delay in starting an animation. For example, when installing CircularPropagation, the View is closer to the established epicenter, the earlier it starts to animate. Set for Transition using the setPropagation parameter.
An example of deleting all the View from the FrameLayout with the Explode Transition and the CircularPropagation installed (the epicenter is a tapa point):

- ChangeImageTransform . Transition, which animates the matrix transition of the image inside the ImageView. With it, you can smoothly resize and scaleType images. Let us consider an example. The girl on the left, we subjected ChangeBounds and ChangeImageTransform operations, while the girl on the right only got ChangeBounds. With the naked eye it is noticeable how the right one “twitches” at the beginning of the animation:

- ChangeClipBounds . Smooth change of the clipBounds parameter in View. But! Since the setClipBounds method appeared only in Android 4.3, then we can only change this parameter and animate it starting from this version. Example:

- Path (Curved) motion . Now it is possible to set for any Transition a rule for calculating the coordinates of parameters consisting of two coordinates using the setPathMotion method. for example, when changing the x and y coordinates, View can run to the new coordinates not along a straight line, but along an arc. To show an example of what it looks like, just give a link to a good article about it .
- A little bit about what else has appeared, but what has not yet been able to port.
Activity Transition , well advertised by Google in the announcements of Lollipop and Material Design. Apparently, a lot of logic is hidden in the Activity itself, even you need to set a special flag. So, directly port it is not so easy. The same situation with the Fragment Transition .
The new Transition under the name ChangeTransform is analogous to the ChangeImageTransform, but applies a matrix transformation to any View.
Targets
For any Transition, you can set many elements - the goals to which it will be applied.
This is done using the methods of the Transition class:
- addTarget (View target) - explicitly set the View for which you want to apply the action
- addTarget (int targetId) - id View
- addTarget (String targetName) - apply only for those View for which the corresponding name was specified via TransitionManager.setTransitionName
- addTarget (Class targetType) - apply only to a specific type of View. for example android.widget.TextView.class
Methods for deleting previously added targets:
- removeTarget (View target)
- removeTarget (int targetId)
- removeTarget (String targetName)
- removeTarget (Class target)
Methods by which you can apply the action "for all but":
- excludeTarget (View target, boolean exclude)
- excludeTarget (int targetId, boolean exclude)
- excludeTarget (Class type, boolean exclude)
- excludeTarget (Class type, boolean exclude)
Methods by which you can exclude all children of a certain layout:
- excludeChildren (View target, boolean exclude)
- excludeChildren (int targetId, boolean exclude)
- excludeChildren (Class type, boolean exclude)
We set Transition through xml
Transition itself can be downloaded from the xml file. Put it in the
res / anim folder. Example
<?xml version="1.0" encoding="utf-8"?> <transitionSet xmlns:app="http://schemas.android.com/apk/res-auto" app:transitionOrdering="together" app:duration="400"> <changeBounds/> <changeImageTransform/> <fade app:fadingMode="fade_in" app:startDelay="200"> <targets> <target app:targetId="@id/transition_title"/> </targets> </fade> </transitionSet>
We have created a TransitionSet set of operations consisting of three other Transitions. At the same time, the previously considered
startDelay property was applied here - the delay time before the start of the animation. The analog for calling from code is
Transition.setStartDelay (long startDelay).
You can load an entire TransitionManager from an xml file, which describes different rules for scene changes depending on the order in which they occur. A simple example:
<?xml version="1.0" encoding="utf-8"?> <transitionManager xmlns:app="http://schemas.android.com/apk/res-auto"> <transition app:toScene="@layout/scene3" app:transition="@anim/scene3_transition"/> </transitionManager>
NoteThe above two examples have been tweaked to work with my Transitions Everywhere library. If you use the system Transition API, then you need to put the xml files in the
res / transition directory and for all attributes you must use the namespace
android: instead of the
app:Custom Transitions
To even more sure what a cool thing these Transitions are, let's write your own Transition.
Suppose we want to change the font size of an animated textView.
(As suggested in the bimeg comments, this example is a bit divorced from reality, because you should not animate the properties, each change of which during the animation causes a requestLayout ())We are required to override the captureStartValues, captureEndValues, and createAnimator methods. The first two are responsible for capturing the values of the required View parameters before and after scene changes. And the createAnimator method, in fact, creates an animator for this parameter change.
private class TextSizeTransition extends Transition {
Result:

PS
Add smooth transitions to Android!
Once again the link to the library:
github.com/andkulikov/transitions-everywhere