“Dynamic” Shared Element Transition: How to hold a common gallery flow
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.
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 gridPreviewActivity
: 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.
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 inGalleryActivity
- Use the
onMapSharedElements
method ofSharedElementCallback
inPreviewActivity
andGalleryActivity
🙇🏻 Digging into to the solution…
- Implement the
onActivityReenter
callback and scroll to the currentRecyclerView
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 theresultCode
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 withFEATURE_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:
- Remove old references for
View
that is not a Shared Element anymore - Add the new ones Shared Element View.
- Setting our custom
SharedElementCallback
that I calledMediaSharedElementCallback
inPreviewActivity
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
inGalleryActivity.onActivityReenter
callback.
In this case I am using a Fragment, so I just delegate to
GalleryFragment.onActivityReenter
💡 Tip:
- Postpone the transition until
RecyclerView
'sonPreDraw
event pass - Find the
ViewHolder
- Update the
SharedElementCallback
with the updated Shared Element View - 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 😉