Kotlin - Animating User Interfaces with the Android Transitions Framework

From Techotopia
Jump to: navigation, search
PreviousTable of ContentsNext
Creating and Managing Overflow Menus on Android with KotlinA Kotlin Android Transition Tutorial using beginDelayedTransition


You are reading a sample chapter from the Android Studio 3.0 / Android 8 Edition book.

Purchase the fully updated Android Studio Hedgehog Kotlin Edition of this publication in eBook or Print format

Android Studio Hedgehog Essentials - Kotlin Edition eBook (PDF)/Print edition contains 94 chapters and over 830 pages
Buy Print Preview Book


The Android Transitions framework was introduced as part of the Android 4.4 KitKat release and is designed to make it easy for you, as an Android developer, to add animation effects to the views that make up the screens of your applications. As will be outlined in both this and subsequent chapters, animated effects such as making the views in a user interface gently fade in and out of sight and glide smoothly to new positions on the screen can be implemented with just a few simple lines of code when using the Transitions framework in Android Studio.


Contents


Introducing Android Transitions and Scenes

Transitions allow the changes made to the layout and appearance of the views in a user interface to be animated during application runtime. While there are a number of different ways to implement Transitions from within application code, perhaps the most powerful mechanism involves the use of Scenes. A scene represents either the entire layout of a user interface screen, or a subset of the layout (represented by a ViewGroup).

To implement transitions using this approach, scenes are defined that reflect the two different user interface states (these can be thought of as the “before” and “after” scenes). One scene, for example, might consist of a EditText, Button and TextView positioned near the top of the screen. The second scene might remove the Button view and move the remaining EditText and TextView objects to the bottom of the screen to make room for the introduction of a MapView instance. Using the transition framework, the changes between these two scenes can be animated so that the Button fades from view, the EditText and TextView slide to the new locations and the map gently fades into view. Scenes can be created in code from ViewGroups, or implemented in layout resource files that are loaded into Scene instances at application runtime.

Transitions can also be implemented dynamically from within application code. Using this approach, scenes are created by referencing collections of user interface views in the form of ViewGroups with transitions then being performed on those elements using the TransitionManager class, which provides a range of methods for triggering and managing the transitions between scenes.

Perhaps the simplest form of transition involves the use of the beginDelayedTransition() method of the TransitionManager class. When called and passed the ViewGroup representing a scene, any subsequent changes to any views within that scene (such as moving, resizing, adding or deleting views) will be animated by the Transition framework.

The actual animation is handled by the Transition framework via instances of the Transition class. Transition instances are responsible for detecting changes to the size, position and visibility of the views within a scene and animating those changes accordingly.

By default, transitions will be animated using a set of criteria defined by the AutoTransition class. Custom transitions can be created either via settings in XML transition files or directly within code. Multiple transitions can be combined together in a TransitionSet and configured to be performed either in parallel or sequentially.

Using Interpolators with Transitions

The Transitions framework makes extensive use of the Android Animation framework to implement animation effects. This fact is largely incidental when using transitions since most of this work happens behind the scenes, thereby shielding the developer from some of the complexities of the Animation framework. One area where some knowledge of the Animation framework is beneficial when using Transitions, however, involves the concept of interpolators.

Interpolators are a feature of the Android Animation framework that allow animations to be modified in a number of pre-defined ways. At present the Animation framework provides the following interpolators, all of which are available for use in customizing transitions:

  • AccelerateDecelerateInterpolator – By default, animation is performed at a constant rate. The AccelerateDecelerateInterpolator can be used to cause the animation to begin slowly and then speed up in the middle before slowing down towards the end of the sequence.
  • AccelerateInterpolator – As the name suggests, the AccelerateInterpolator begins the animation slowly and accelerates at a specified rate with no deceleration at the end.
  • AnticipateInterpolator – The AnticipateInterpolator provides an effect similar to that of a sling shot. The animated view moves in the opposite direction to the configured animation for a short distance before being flung forward in the correct direction. The amount of backward force can be controlled through the specification of a tension value.
  • AnticipateOvershootInterpolator – Combines the effect provided by the AnticipateInterpolator with the animated object overshooting and then returning to the destination position on the screen.
  • BounceInterpolator – Causes the animated view to bounce on arrival at its destination position.
  • CycleInterpolator – Configures the animation to be repeated a specified number of times.
  • DecelerateInterpolator – The DecelerateInterpolator causes the animation to begin quickly and then decelerate by a specified factor as it nears the end.
  • LinearInterpolator – Used to specify that the animation is to be performed at a constant rate.
  • OvershootInterpolator – Causes the animated view to overshoot the specified destination position before returning. The overshoot can be configured by specifying a tension value.

As will be demonstrated in this and later chapters, interpolators can be specified both in code and XML files.

You are reading a sample chapter from the Android Studio 3.0 / Android 8 Edition book.

Purchase the fully updated Android Studio Hedgehog Kotlin Edition of this publication in eBook or Print format

Android Studio Hedgehog Essentials - Kotlin Edition eBook (PDF)/Print edition contains 94 chapters and over 830 pages
Buy Print Preview Book


Working with Scene Transitions

Scenes can be represented by the content of an Android Studio XML layout file. The following XML, for example, could be used to represent a scene consisting of three button views within a RelativeLayout parent:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/RelativeLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
 
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:onClick="goToScene2"
        android:text="@string/one_string" />
 
    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:onClick="goToScene1"
        android:text="@string/two_string" />
 
    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="@string/three_string" />
 
</RelativeLayout>

Assuming that the above layout resides in a file named scene1_layout.xml located in the res/layout folder of the project, the layout can be loaded into a scene using the getSceneForLayout() method of the Scene class. For example:

val scene1 = Scene.getSceneForLayout(rootContainer, 
                R.layout.scene1_layout, this)

Note that the method call requires a reference to the root container. This is the view at the top of the view hierarchy in which the scene is to be displayed.

To display a scene to the user without any transition animation, the enter() method is called on the scene instance:

scene1.enter()

Transitions between two scenes using the default AutoTransition class can be triggered using the go() method of the TransitionManager class:

TransitionManager.go(scene2)

Scene instances can be created easily in code by bundling the view elements into one or more ViewGroups and then creating a scene from those groups. For example:

val scene1 = Scene(viewGroup1)
val scene2 = Scene(viewGroup2, viewGroup3)

Custom Transitions and TransitionSets in Code

The examples outlined so far in this chapter have used the default transition settings in which resizing, fading and motion are animated using pre-configured behavior. These can be modified by creating custom transitions which are then referenced during the transition process. Animations are categorized as either change bounds (relating to changes in the position and size of a view) and fade (relating to the visibility or otherwise of a view).

A single Transition can be created as follows:

val myCchangeBounds = ChangeBounds()

This new transition can then be used when performing a transition:

TransitionManager.go(scene2, myChangeBounds)

Multiple transitions may be bundled together into a TransitionSet instance. The following code, for example, creates a new TransitionSet object consisting of both change bounds and fade transition effects:

val myTransition = TransitionSet()
myTransition.addTransition(ChangeBounds())
myTransition.addTransition(Fade())

Transitions can be configured to target specific views (referenced by view ID). For example, the following code will configure the previous fade transition to target only the view with an ID that matches myButton1:

val myTransition = TransitionSet()
myTransition.addTransition(ChangeBounds())
val fade = Fade()
fade.addTarget(R.id.myButton1)
myTransition.addTransition(fade)

Additional aspects of the transition may also be customized, such as the duration of the animation. The following code specifies the duration over which the animation is to be performed:

val changeBounds = ChangeBounds()
changeBounds.duration = 2000

As with Transition instances, once a TransitionSet instance has been created, it can be used in a transition via the TransitionManager class. For example:

TransitionManager.go(scene1, myTransition)

You are reading a sample chapter from the Android Studio 3.0 / Android 8 Edition book.

Purchase the fully updated Android Studio Hedgehog Kotlin Edition of this publication in eBook or Print format

Android Studio Hedgehog Essentials - Kotlin Edition eBook (PDF)/Print edition contains 94 chapters and over 830 pages
Buy Print Preview Book

Custom Transitions and TransitionSets in XML

While custom transitions can be implemented in code, it is often easier to do so via XML transition files using the <fade> and <changeBounds> tags together with some additional options. The following XML includes a single changeBounds transition:

<?xml version="1.0" encoding="utf-8"?>
<changeBounds/>

As with the code based approach to working with transitions, each transition entry in a resource file may be customized. The XML below, for example, configures a duration for a change bounds transition:

<changeBounds android:duration="5000" >

Multiple transitions may be bundled together using the <transitionSet> element:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet 
  xmlns:android="http://schemas.android.com/apk/res/android" >
 
  <fade
    android:duration="2000"
    android:fadingMode="fade_out" />
 
  <changeBounds 
    android:duration="5000" >
     
    <targets>
      <target android:targetId="@id/button2" />
    </targets>
      
  </changeBounds>
  
  <fade
    android:duration="2000"
    android:fadingMode="fade_in" />
</transitionSet>

Transitions contained within an XML resource file should be stored in the res/transition folder of the project in which they are being used and must be inflated before being referenced in the code of an application. The following code, for example, inflates the transition resources contained within a file named transition.xml and assigns the results to a reference named myTransition:

val myTransition = TransitionInflater.from(this)
                .inflateTransition(R.transition.transition)

Once inflated, the new transition can be referenced in the usual way:

TransitionManager.go(scene1, myTransition)

By default, transition effects within a TransitionSet are performed in parallel. To instruct the Transition framework to perform the animations sequentially, add the appropriate android:transitionOrdering property to the transitionSet element of the resource file:

<?xml version="1.0" encoding="utf-8"?>
 
<transitionSet 
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:transitionOrdering="sequential">
 
    <fade
      android:duration="2000"
      android:fadingMode="fade_out" />
 
    <changeBounds 
      android:duration="5000" >
  </changeBounds>
</transitionSet>

Change the value from “sequential” to “together” to indicate that the animation sequences are to be performed in parallel.

You are reading a sample chapter from the Android Studio 3.0 / Android 8 Edition book.

Purchase the fully updated Android Studio Hedgehog Kotlin Edition of this publication in eBook or Print format

Android Studio Hedgehog Essentials - Kotlin Edition eBook (PDF)/Print edition contains 94 chapters and over 830 pages
Buy Print Preview Book

Working with Interpolators

As previously discussed, interpolators can be used to modify the behavior of a transition in a variety of ways and may be specified either in code or via the settings within a transition XML resource file.

When working in code, new interpolator instances can be created by calling the constructor method of the required interpolator class and, where appropriate, passing through values to further modify the interpolator behavior:

AccelerateDecelerateInterpolator()
AccelerateInterpolator(float factor)
AnticipateInterpolator(float tension)
AnticipateOvershootInterpolator(float tension)
BounceInterpolator()
CycleInterpolator(float cycles)
DecelerateInterpolator(float factor)
LinearInterpolator()
OvershootInterpolator(float tension)

Once created, an interpolator instance can be attached to a transition using the setInterpolator() method of the Transition class. The following code, for example, adds a bounce interpolator to a change bounds transition:

val changeBounds = ChangeBounds()
changeBounds.interpolator = BounceInterpolator()

Similarly, the following code adds an accelerate interpolator to the same transition, specifying an acceleration factor of 1.2:

changeBounds.interpolator = AccelerateInterpolator(1.2f)

In the case of XML based transition resources, a default interpolator is declared using the following syntax:

android:interpolator="@android:anim/<interpolator_element>"

In the above syntax, <interpolator_element> must be replaced by the resource ID of the corresponding interpolator selected from the following list:

accelerate_decelerate_interpolator
accelerate_interpolator
anticipate_interpolator
anticipate_overshoot_interpolator
bounce_interpolator
cycle_interpolator
decelerate_interpolator
linear_interpolator
overshoot_interpolator

The following XML fragment, for example, adds a bounce interpolator to a change bounds transition contained within a transition set:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet 
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:transitionOrdering="sequential">
 
  <changeBounds
    android:interpolator="@android:anim/bounce_interpolator"
    android:duration="2000" />
 
  <fade
    android:duration="1000"
    android:fadingMode="fade_in" />
</transitionSet>

This approach to adding interpolators to transitions within XML resources works well when the default behavior of the interpolator is required. The task becomes a little more complex when the default behavior of an interpolator needs to be changed. Take, for example, the cycle interpolator. The purpose of this interpolator is to make an animation or transition repeat a specified number of times. In the absence of a cycles attribute setting, the cycle interpolator will perform only one cycle. Unfortunately, there is no way to directly specify the number of cycles (or any other interpolator attribute for that matter) when adding an interpolator using the above technique. Instead, a custom interpolator must be created and then referenced within the transition file.

Creating a Custom Interpolator

A custom interpolator must be declared in a separate XML file and stored within the res/anim folder of the project. The name of the XML file will be used by the Android system as the resource ID for the custom interpolator.

Within the custom interpolator XML resource file, the syntax should read as follows:

<?xml version="1.0" encoding="utf-8"?>
<interpolatorElement xmlns:android="http://schemas.android.com/apk/res/android" android:attribute="value" />

In the above syntax, interpolatorElement must be replaced with the element name of the required interpolator selected from the following list:

accelerateDecelerateInterpolator
accelerateInterpolator
anticipateInterpolator
anticipateOvershootInterpolator
bounceInterpolator
cycleInterpolator
decelerateInterpolator
linearInterpolator
overshootInterpolator

The attribute keyword is replaced by the name attribute of the interpolator for which the value is to be changed (for example tension to change the tension attribute of an overshoot interpolator). Finally, value represents the value to be assigned to the specified attribute. The following XML, for example, contains a custom cycle interpolator configured to cycle 7 times:

<?xml version="1.0" encoding="utf-8"?>
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android" android:cycles="7" />

Assuming that the above XML was stored in a resource file named my_cycle.xml stored in the res/anim project folder, the custom interpolator could be added to a transition resource file using the following XML syntax:

<changeBounds 
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:duration="5000"
  android:interpolator="@anim/my_cycle" >

Using the beginDelayedTransition Method

Perhaps the simplest form of Transition based user interface animation involves the use of the beginDelayedTransition() method of the TransitionManager class. This method is passed a reference to the root view of the viewgroup representing the scene for which animation is required. Subsequent changes to the views within that sub view will then be animated using the default transition settings:

TransitionManager.beginDelayedTransition(myLayout)
// Make changes to the scene here

If behavior other than the default animation behavior is required, simply pass a suitably configured Transition or TransitionSet instance through to the method call:

TransitionManager.beginDelayedTransition(myLayout, myTransition)

Summary

The Android 4.4 KitKat SDK release introduced the Transition Framework, the purpose of which is to simplify the task of adding animation to the views that make up the user interface of an Android application. With some simple configuration and a few lines of code, animation effects such as movement, visibility and resizing of views can be animated by making use of the Transition framework. A number of different approaches to implementing transitions are available involving a combination of Kotlin code and XML resource files. The animation effects of transitions may also be enhanced through the use of a range of interpolators.

Having covered some of the theory of Transitions in Android, the next two chapters will put this theory into practice by working through some example Android Studio based transition implementations.


You are reading a sample chapter from the Android Studio 3.0 / Android 8 Edition book.

Purchase the fully updated Android Studio Hedgehog Kotlin Edition of this publication in eBook or Print format

Android Studio Hedgehog Essentials - Kotlin Edition eBook (PDF)/Print edition contains 94 chapters and over 830 pages
Buy Print Preview Book



PreviousTable of ContentsNext
Creating and Managing Overflow Menus on Android with KotlinA Kotlin Android Transition Tutorial using beginDelayedTransition