Animation / Scene / Transition

Transition Animations – Part 2

In the previous article we began looking at the new Transition Animations API introduced in KitKat 4.4. In this article we’ll have a look beyond the basics and explore how we can take greater control over things using Scenes.

Styling AndroidIn the previous article we used TransitionManager‘s beginDelayedTransition method to perform some rather impressive transitions between two states of our view hierarchy. It did this by matching the view IDs of the children within the view hierarchy and building and running an AnimationSet to transition between those two states. While we got impressive results for adding minimal code to perform the transitions, we did have to do some do some manipulation of the objects within the hierarchy in order to get that to work. However there is another way that we can achieve the exact same result which uses some rather more familiar techniques.

The technique actually works in a very similar way in terms of what the TransitionManger does, but we use a rather different mechanism to define the static view states by using Scenes. A Scene is what we have been referring to up to now as a view state, in other words a static state of the view hierarchy. We can also represent a static view state as a layout, and a Scene is simply an encapsulation of a static layout which enables the TransitionManager to perform the transition between these two Scenes.

We can create a Scene using a constructor, but if we’re using LayoutInflator to inflate our layout, we can actually perform this in a single transaction:

private ViewGroup mContainer;
private TransitionManager mTxManager;

private Scene mScene1;
private Scene mScene2;
private Scene mScene3;

@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);
	mTxManager = new TransitionManager();
	Transition transition = new ChangeBounds();
	mScene1 = Scene.getSceneForLayout(mContainer, 
		R.layout.grid1, getActivity());
	mScene1.setEnterAction(new Runnable() {
		@Override
		public void run() {
			mScene1.getSceneRoot().findViewById(R.id.item2)
				.setOnClickListener(Part2.this);
			mScene1.getSceneRoot().findViewById(R.id.item3)
				.setOnClickListener(Part2.this);
		}
	});
	.
	.
	.
}

The first this that this does is inflate a simple layout which contains a single FrameLayout which we’ll use as a parent for everything else. Next we create a TransitionManager instance, and then we create our Scene using getSceneForLayout. This takes tree arguments: The container; the ID of the layout; and the Context.

The next thing that we need to do it to set click listeners so that we can respond to click events, but the layout will not have been inflated yet, so a findViewById will return null. However the Scene enables us to attach a Runnable which will be called whenever the Scene is entered, and this is the perfect place to do this. We can also attach an exit Runnable if we need to to perform any cleanup, but in this case we don’t need to.

So that is how we define a Scene. However a single Scene on its own is not much use so we have to define the others. We have three stable states:

  1. Item 1 is selected, and the unselected items are Item 2 and Item 3 (in that order);
  2. Item 2 is selected, and the unselected items are Item 1 and Item 3 (in that order);
  3. and Item 3 is selected, and the unselected items are Item 1 and Item 3 (in that order).

We have already inflated the first state in to mScene1, so we inflate the mScene2 and mScene3 from their respective layouts setting click listeners on the two unselected items. This code is almost identical to what we’ve done for mScene1, so I won’t include it here, but it can be found in the source.

Now that we have our scenes defined, we need to define how we transition between them:

@Override
public View onCreateView(LayoutInflater inflater, 
		ViewGroup container, Bundle savedInstanceState) {
	.
	.
	.
	Transition transition = new ChangeBounds();
	mTxManager.setTransition(mScene1, mScene2, transition);
	mTxManager.setTransition(mScene1, mScene3, transition);
	mTxManager.setTransition(mScene2, mScene1, transition);
	mTxManager.setTransition(mScene2, mScene3, transition);
	mTxManager.setTransition(mScene3, mScene1, transition);
	mTxManager.setTransition(mScene3, mScene2, transition);
	mScene1.enter();
	return view;
}

First we declare the transition that we want to perform. We’ll use the same ChangeBounds transition that we used previously. Then we need to register each of the transition permutations between the three scene. As each scene can transition to either of the other two, this makes 6 transitions in total. We use the same transition for each, but here is where the real power of TransitionManager becomes apparent: we could define different transitions between different scenes.

The final thing that we need to do he is enter mScene1. The enter() method will go to the Scene from which it is called without performing any transitions. We want to do this because when the Fragment containing this example is first loaded, we want to display mScene1 but don’t want any animations at this point.

Our onCreateView is a little more complex than the previous example, but there is nothing here which is particularly difficult. However we can see some benefits of this method when we look at our onClick() implementation, which is much simpler than before:

@Override
public void onClick(View v) {
	switch (v.getId()) {
		case R.id.item1:
			mTxManager.transitionTo(mScene1);
			break;
		case R.id.item2:
			mTxManager.transitionTo(mScene2);
			break;
		case R.id.item3:
			mTxManager.transitionTo(mScene3);
			break;
	}
}

All we do is transition to the appropriate Scene when one of the views is clicked. The transitionTo() method will do the same as the Transition.enter() method, except that this will perform the transition animation.

That’s it. If we run this we’ll get exactly the same behaviour as the second example in the previous article:

Once again, we have achieved a nice transition without going near any of the traditional animation classes. However, we’ve only used a single transition type: ChanngeBounds. In the next article in this series we’ll have a look at what else is available to us.

The source code for this article is available here.

© 2013 – 2014, Mark Allison. All rights reserved.

Copyright © 2013 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

4 Comments

  1. As always great post!
    I was wondering if that is supported on earlier versions of android. And if no haw can I manage that?
    Thanks for your answer!

      1. Thanks! If I need to support API 10 how can I manage the presence of these animations in my app running on a Gingerbread device?

        1. The short answer is: not easily. It may be possible by adapting Stéphane’s library using Jake Wharton’s NineOldAndroids library to get property animations back ported to API10. But you’re going to have quite a chunk of work to do it.

          I would recommend targeting API level 14 and later. Targeting API 10 will require *much* more work, not just for transition animations, but in general.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.