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 which we just use strings.xml
(We don't want any spelling or typing mistakes!)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