In part 1 we learned what Shared Element Transitions are, how they work and a simple example using Activity to Activity.

In part 2 we're going to learn a little more on how they should be used and then do a Fragment to Fragment example.

Guidelines

There are a few things that should be kept in mind when using Shared Element Transitions. These come from the Material motion guidelines mentioned in part 1.

  • While we want the user to marvel at our fantastic new animation, we don't want to impede them on their journey so it's important we animate quickly. If we stagger too many animations or slow it down we're not creating a great user experience.

  • Don't do too many Shared Element Transitions at once. When transitioning we want the intention to be clear as possible. Too many Shared Elements potentially crossing paths or moving in different directions can just be confusing.

  • Be consistent. If you're using Shared Element Transitions in only one part of your app and you leave out other prime candidates, you're going to have negative impact on the users feelings towards it. It will feel incomplete and will be worse than having no Shared Element Transitions at all. As above though, this doesn't mean go crazy, but by keeping consistency you will help users understand what they mean and their intention.

To see some prime examples of the above scenarios head here.

Fragment to Fragment

To get started let's create a simple Activity to hold our Fragment.

public class FragmentToFragmentActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_to_fragment);

        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.content, SimpleFragmentA.newInstance())
                .commit();
    }
}

Nothing special here. We're just launching our SimpleFragmentA in onCreate of our FragmentToFragmentActivity by adding it in a Transaction.

Now we need to setup our SimpleFragmentA. First of all lets make sure our ImageView in our layout XML has a transitionName.

    <ImageView
        android:id="@+id/fragment_a_imageView"
        android:layout_width="128dp"
        android:layout_height="96dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="80dp"
        android:scaleType="centerCrop"
        android:src="@drawable/gorilla"
        android:transitionName="@string/simple_fragment_transition" />

Just like the Activity example we need to set a transitionName (line 10) on BOTH of the views we intend to be part of our Shared Element Transition. Now to the actual Fragment itself.

    ...

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        final ImageView imageView = (ImageView) view.findViewById(R.id.fragment_a_imageView);

        Button button = (Button) view.findViewById(R.id.fragment_a_btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SimpleFragmentB simpleFragmentB = SimpleFragmentB.newInstance();
                getFragmentManager()
                        .beginTransaction()
                        .addSharedElement(imageView, ViewCompat.getTransitionName(imageView))
                        .addToBackStack(TAG)
                        .replace(R.id.content, simpleFragmentB)
                        .commit();
            }
        });
    }

Our main concern in SimpleFragmentA is the onViewCreated method. On line 7 we're getting hold of our ImageView from the layout. We'll need this for the Shared Element Transition. Line 9 we're getting our button and in line 8 we're setting an onClickListener to get clicks to launch SimpleFragmentB. Line 14 is where things get interesting.

We create a Transaction as is normal to launch a new Fragment. On line 16 we actually call addSharedElement and, much like the Activity to Activity example from part 1, we need to give our View and transitionName as parameters. So we give our ImageView and our transitionName which is located in strings.xml (We don't want any spelling or typing mistakes!) which we just use ViewCompat.getTransitionName as we know the transitionName in both layouts is the same. Remember, it doesn't have to be and only needs to be unique in the view hierarchy.

Line 17 we call addToBackStack. Why? Well otherwise we won't get the nice Shared Element Transition when we press back. If this line is left out, when you press back it would just finish the FragmentToFragmentActivity as there's no history (or nothing in the back stack).

Line 18 we call replace as we want SimpleFragmentB to completely replace SimpleFragmentA.

Again not too different to how you would normally launch a Fragment. Finally lets get SimpleFragmentB setup.

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move));
        }
    }

Apart from inflating our layout (not shown here) and adding a transitionName to the ImageView in our layout xml (as above in SimpleFragmentA), this is the only other thing we need to do.

So all we need to do is call setSharedElementTransition and pass in a Transition by inflating one provided to us from resources. This is only available on 21+, which is why we wrap it in an if. By default the "move" transition combines 4 different types.

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">  
    <changeBounds/>
    <changeTransform/>
    <changeClipBounds/>
    <changeImageTransform/>
</transitionSet>  

We'll go into these in a bit more detail in a future post. But for now, by using these, we're using the same default style of Transition that we used in the Activity to Activity example in part 1.

And that's it. If we run it, we should have something identical to part 1 except with a Gorilla 😃

The source code for part 2 can be found here alongside part 1.

In part 3 we'll look at how we can use Picasso and Glide with Shared Element Transitions.

Update (5/03/17): I've made some minor adjustments to this post since first releasing. There's nothing better than having 1000's of eyes on your work for reviewing!

Thanks to Wesley Ellis, Dimitris Karittevlis and Marcos Holgado for proof reading