📜 ⬆️ ⬇️

Animations using the Transitions API

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); //  ,   ,        sceneRoot TransitionManager.beginDelayedTransition(sceneRoot); //     ViewGroup.LayoutParams params = square.getLayoutParams(); params.width = newSquareSize; params.height = newSquareSize; square.setLayoutParams(params); 

Result:
image

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



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); // You can also inflate a generate a Scene from a layout resource file. final Scene scene2 = Scene.getSceneForLayout(sceneRoot, R.layout.scene3, this); findViewById(R.id.btn_change_scene).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //    AutoTransition TransitionSet set = new TransitionSet(); set.addTransition(new Fade()); set.addTransition(new ChangeBounds()); //     set.setOrdering(TransitionSet.ORDERING_TOGETHER); //     set.setDuration(500); //   Interpolator set.setInterpolator(new AccelerateInterpolator()); TransitionManager.go(scene2, set); } }); 

Result:
image

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/TransitionsBackport
github.com/pardom/TransitionSupportLibrary
What 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






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:

Methods for deleting previously added targets:

Methods by which you can apply the action "for all but":

Methods by which you can exclude all children of a certain layout:

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> 

Note
The 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 { //  ,     HashMap  private static final String PROPNAME_TEXT_SIZE = "textSizeTransition:textSize"; private void captureValues(TransitionValues transitionValues) { //    TextView if (transitionValues.view instanceof TextView) { TextView textView = ((TextView) transitionValues.view); //    ,     dip transitionValues.values.put(PROPNAME_TEXT_SIZE, textView.getTextSize() / getResources().getDisplayMetrics().density); } } @Override public void captureStartValues(TransitionValues transitionValues) { captureValues(transitionValues); } @Override public void captureEndValues(TransitionValues transitionValues) { captureValues(transitionValues); } @Override public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { //    ,           //        TextView. // endValues   null, ,      View  if (startValues != null && endValues != null && endValues.view instanceof TextView) { TextView textView = (TextView) endValues.view; //   float start = (Float) startValues.values.get(PROPNAME_TEXT_SIZE); float end = (Float) endValues.values.get(PROPNAME_TEXT_SIZE); if (start != end) { //    View   textView.setTextSize(start); //      return ObjectAnimator.ofFloat(textView, "textSize", start, end); } } return null; } } 

Result:
image

PS


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

Source: https://habr.com/ru/post/243363/


All Articles