Transition Animations – Part 3

In the previous article we looked at Scenes and how they can be used to encapsulate static view hierarchy states, however we’ve only looked at moving items around. In this article we’ll look at different use cases for our animations and see how we can use different transitions.

Let’s start by creating two new layouts which we’ll use for two new Scenes. The purpose of these will be to demonstrate different kinds of animations that we may wish to use. The first is three items inside a vertical LinearLayout.

XHTML

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

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

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/parent"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_weight="1"

android:gravity="center"

android:orientation="vertical"

android:padding="@dimen/margin">

<TextView

android:id="@+id/item_1a"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:minHeight="@dimen/item_height"

android:background="@color/red"

android:text="Item 1a"

android:gravity="center"

android:textAppearance="@style/Text"

android:layout_margin="@dimen/margin"/>

<TextView

android:id="@+id/item_1b"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:minHeight="@dimen/item_height"

android:background="@color/green"

android:text="Item 1b"

android:gravity="center"

android:textAppearance="@style/Text"

android:layout_margin="@dimen/margin"/>

<TextView

android:id="@+id/item_1c"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:minHeight="@dimen/item_height"

android:background="@color/blue"

android:text="Item 1c"

android:gravity="center"

android:textAppearance="@style/Text"

android:layout_margin="@dimen/margin"/>

</LinearLayout>

The second has only two items (the middle item has disappeared), and the bottom item has moved to the top and changed colour.

XHTML

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

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

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/parent"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_weight="1"

android:gravity="center"

android:orientation="vertical"

android:padding="@dimen/margin">

<TextView

android:id="@+id/item_1c"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:minHeight="@dimen/item_height"

android:background="@color/orange"

android:text="Item 1c"

android:gravity="center"

android:textAppearance="@style/Text"

android:layout_margin="@dimen/margin"/>

<TextView

android:id="@+id/item_1a"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:minHeight="@dimen/item_height"

android:background="@color/red"

android:text="Item 1a"

android:gravity="center"

android:textAppearance="@style/Text"

android:layout_margin="@dimen/margin"/>

</LinearLayout>

The code for switching between these Scenes is very similar to what we did in the previous article:

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

privateViewGroup mContainer;

privateTransitionManager mTxManager;

privateScene mScene1;

privateScene mScene2;

@Override

publicView onCreateView(LayoutInflater inflater,

ViewGroup container,Bundle savedInstanceState){

View view=inflater.inflate(R.layout.fragment_part2,

container,false);

mContainer=(ViewGroup)view.findViewById(R.id.container);

mScene1=Scene.getSceneForLayout(mContainer,

R.layout.part3_1,getActivity());

mScene1.setEnterAction(newRunnable(){

@Override

publicvoidrun(){

mScene1.getSceneRoot()

.findViewById(R.id.item_1c)

.setOnClickListener(Part3.this);

}

});

mScene2=Scene.getSceneForLayout(mContainer,

R.layout.part3_2,getActivity());

mScene2.setEnterAction(newRunnable(){

@Override

publicvoidrun(){

mScene2.getSceneRoot()

.findViewById(R.id.item_1a)

.setOnClickListener(Part3.this);

}

});

mTxManager=newTransitionManager();

Transition transition=newChangeBounds();

mTxManager.setTransition(mScene1,mScene2,transition);

mTxManager.setTransition(mScene2,mScene1,transition);

mScene1.enter();

returnview;

}

@Override

publicvoidonClick(Viewv){

switch(v.getId()){

caseR.id.item_1a:

mTxManager.transitionTo(mScene1);

break;

caseR.id.item_1c:

mTxManager.transitionTo(mScene2);

break;

}

}

If we use ChangeBounds as we have in the previous examples, we see the following:

The first and third items are switching places as we have seen previously, but the middle item is simply appearing and disappearing as we transition. But what if we want to do something a little different with the middle item? Up to now we’ve only used the ChangeBounds transition, but there are actually others that we can use. One which could be useful for the middle item is Fade which will adjust the alpha values to change the transparency during the transition to fade items in and out:

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

@Override

public View onCreateView(LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fragment_part2,

container, false);

mContainer = (ViewGroup)view.findViewById(R.id.container);

mScene1 = Scene.getSceneForLayout(mContainer,

R.layout.part3_1, getActivity());

mScene1.setEnterAction(new Runnable() {

@Override

public void run() {

mScene1.getSceneRoot()

.findViewById(R.id.item_1c)

.setOnClickListener(Part3.this);

}

});

mScene2 = Scene.getSceneForLayout(mContainer,

R.layout.part3_2, getActivity());

mScene2.setEnterAction(new Runnable() {

@Override

public void run() {

mScene2.getSceneRoot()

.findViewById(R.id.item_1a)

.setOnClickListener(Part3.this);

}

});

mTxManager = new TransitionManager();

Transition transition = new Fade();

mTxManager.setTransition(mScene1, mScene2, transition);

mTxManager.setTransition(mScene2, mScene1, transition);

mScene1.enter();

return view;

}

This changes things quite dramatically:

The middle item now fades in and out as we transition between Scenes. However the first and third items now jump between positions. The reason for this is that different types of transitions affect views in different ways. ChangeBounds identifies Views which will move between Scenes and performs the appropriate transition; Fade on the other hand identifies views whose visibility changes and performs the appropriate transition upon them.

That’s all well and good but what if we want to combine these two transition types so that we perform appropriate animations on both the views whose position changes between Scenes, and those whose visibility changes? This sounds like a job for AutoTransition:

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

@Override

public View onCreateView(LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fragment_part2,

container, false);

mContainer = (ViewGroup)view.findViewById(R.id.container);

mScene1 = Scene.getSceneForLayout(mContainer,

R.layout.part3_1, getActivity());

mScene1.setEnterAction(new Runnable() {

@Override

public void run() {

mScene1.getSceneRoot()

.findViewById(R.id.item_1c)

.setOnClickListener(Part3.this);

}

});

mScene2 = Scene.getSceneForLayout(mContainer,

R.layout.part3_2, getActivity());

mScene2.setEnterAction(new Runnable() {

@Override

public void run() {

mScene2.getSceneRoot()

.findViewById(R.id.item_1a)

.setOnClickListener(Part3.this);

}

});

mTxManager = new TransitionManager();

Transition transition = new AutoTransition();

mTxManager.setTransition(mScene1, mScene2, transition);

mTxManager.setTransition(mScene2, mScene1, transition);

mScene1.enter();

return view;

}

AutoTransition does precisely what we need, it combines both ChangeBounds and Fade transitions to move the views whose location changes and fade in and out the views whose visibility changes:

One thing worth noting is that it performs the fade out of disappearing views first, then it performs the move, and then it performs the fade in of appearing views. Have a look at the source code of AutoTransition. (It’s really very simple to understand).

AutoTransition extends TransitionSet. Those who are familiar with both the View and Property Animation frameworks will understand the concept of Sets, which allow us to combine individual animations. All that’s happening here is that AutoTransition combines ChangeBounds and Fade and executes them sequentially. We could change this behaviour by calling the setOrdering() method of AutoTransition. Alternatively we could define our own TransitionSet to tweak things:

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

@Override

public View onCreateView(LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fragment_part2,

container, false);

mContainer = (ViewGroup)view.findViewById(R.id.container);

mScene1 = Scene.getSceneForLayout(mContainer,

R.layout.part3_1, getActivity());

mScene1.setEnterAction(new Runnable() {

@Override

public void run() {

mScene1.getSceneRoot()

.findViewById(R.id.item_1c)

.setOnClickListener(Part3.this);

}

});

mScene2 = Scene.getSceneForLayout(mContainer,

R.layout.part3_2, getActivity());

mScene2.setEnterAction(new Runnable() {

@Override

public void run() {

mScene2.getSceneRoot()

.findViewById(R.id.item_1a)

.setOnClickListener(Part3.this);

}

});

mTxManager = new TransitionManager();

TransitionSet transition = new TransitionSet();

transition.setOrdering(TransitionSet.ORDERING_TOGETHER);

transition.addTransition(new ChangeBounds())

.addTransition(new Fade());

mTxManager.setTransition(mScene1, mScene2, transition);

mTxManager.setTransition(mScene2, mScene1, transition);

mScene1.enter();

return view;

}

Running this gives the following result:

That’s it for the transition types that are currently shipped in KitKat, but if we look at the KitKat source we can see that there are some additional ones currently in progress: ChangeText, Crossfade, Recolor, Rotate, and Slide. I have tried playing with these with limited success, so be prepared to have to tinker if you want to have a play with them. Incidentally, these are also included in Stéphane Guérin’s back-port of the Transitions APIs.

All of the examples that we’ve looked at using ChangeBounds have used layout where the children being animated all have a common direct parent – the parent layout. I mentioned earlier on in this series that this was an issue, and in the next article we’ll look deeper in to this at look at the possibilities for performing ChangeBounds transitions when the children do not have a common parent.