“Dynamic” Shared Element Transition: How to hold a common gallery flow

André Mion
AndroidPub
Published in
5 min readJul 17, 2017

--

A typical flow of a gallery app that changes the Shared Element after a Window Transition.

🌄 How did I get there?

Many apps that use images has some kind of feature to pick images from a gallery in order to add them in the app.

You can choose to use a default Intent to pick image and pray for user having some gallery app that resolves that Intent (usually all manufacturer have a default gallery app embedded).

And when you have to pick more than one image?

In most of case you don’t have an embedded solution or you have different user experiences that differ per app…

And when you have the same feature in more and more projects?

It’s better to build a library to do this.

*Images from Google Image Search

And that’s what I did... A kind of library to just pick images from gallery.

😯 The discovery of issue…

Basically the Louvre has two Activities:

  • GalleryActivity: That shows the images in a grid
  • PreviewActivity: Shows the clicked image in full screen mode and can you can swipe horizontally between those images

After I have coded the transition to PreviewActivity, I figured out that backing to GalleryActivity, after having swiped to another one, occurred an issue that the ordinary setup of a Window Transition doesn’t solve it.

Some issues found that the ordinary setup of a Window Transition doesn’t solve it

I called that flow of:

“Dynamic” Shared Element Transition

Coding to this was not a trivial task and I had to add some extra code to solve it.

🤔 How can I fixed that?

When you start an Activity using transitions like this:

The framework maps the shared elements and keep the mapping until you back to the previous Activity.

The transition has callbacks that help us to manage the transitions and they will be enough to cover general flows.

But there are some additional callbacks to deal with more advanced transitions.

With those callbacks, all you have to do is updating the Transition Shared Element on PreviewActivity every time you swipe, and pass some information to the previous GalleryActivity.

To cover the “Dynamic” Shared Element you need to take basically these steps:

  • Use the onActivityReenter callback in GalleryActivity
  • Use the onMapSharedElements method of SharedElementCallback in PreviewActivity and GalleryActivity

🙇🏻 Digging into to the solution…

  • Implement the onActivityReenter callback and scroll to the current RecyclerView element position

The onActivityReenter callback is:

Called when an activity you launched with an activity transition exposes this Activity through a returning activity transition, giving you the resultCode and any additional data from it.

*This method will only be called if the activity set a result code other than RESULT_CANCELED and it supports activity transitions with FEATURE_ACTIVITY_TRANSITIONS.

Set that result when you are going to leave the PreviewActivity

You are already positioning the RecyclerView element correctly but the transition expected is not working.

So I present you SharedElementCallback:

Used in both Enter or Exit transition to monitor the Shared Element. The events can be used to customize Activity and Fragment Transition Behaviour.

For you flow, the most important method that you will need is:

Lets the SharedElementCallback adjust the mapping of shared element names to Views.

names The names of all shared elements transferred from the calling Activity or Fragment in the order they were provided.

sharedElements The mapping of shared element names to Views. The best guess will be filled into sharedElements based on the transitionNames.

You can use a SharedElementCallback instance and keep an updated reference to Shared Element Views that are current being shown on screen, for instance, when you swiping the ViewPager.

You can create a method like this:

When Transition API calls onMapSharedElements we will do two things:

  1. Remove old references for View that is not a Shared Element anymore
  2. Add the new ones Shared Element View.

And call sharedElementCallback.setSharedElementViews(imageView, checkbox) in setPrimaryItem method of our PagerAdapter

OK, you have the share element updated in PreviewActivity but the Transition API doesn’t match with the shared element in GalleryActivity.

You need to use our custom SharedElementCallback in GalleryActivity as well.

  • Set SharedElementCallback in GalleryActivity.onActivityReenter callback.

In this case I am using a Fragment, so I just delegate to GalleryFragment.onActivityReenter

💡 Tip:

  1. Postpone the transition until RecyclerView's onPreDraw event pass
  2. Find the ViewHolder
  3. Update the SharedElementCallback with the updated Shared Element View
  4. And then start the postponed transition.

We are already there. We still have some minor issue when we are starting a new transition after we have backed from Preview.

This issue happened because the Window Transition API use the last ExitSharedElementCallback that we set in onActivityReenter.

What we must do is:

  • Nulling that SharedElementCallback reference

A good time to do that is when the Exit Shared Element Transition is finished:

And finally we have our transition working fine 🍻🏖

Sometimes that tool which can solve our problems already exists but is “hidden”.

Some non traditional flow that uses motion design can have a native solution already there. Look for that options and in last case, think in building a entire solution from scratch.

You can find more details about that solution below:

If you think that this post is useful and helped you, 👊🏻 ♡ and share it so you can help other people as well 😉

📚 References

--

--

André Mion
AndroidPub

🇧🇷 Android Engineer living in 🇵🇹 • Full time Husband and Dad • Occasionally Drummer and Inline Skater… I write mostly about Android and Tech…