ViewPager Fragments – Shared Element Transitions

11,029

Solution 1

From what I can tell (and correct me if I'm wrong), what you are trying to achieve is basically something like this: Assume you have a MainActivity, a DetailsActivity, and an arbitrary set of images. The MainActivity displays the set of images in a grid and the DetailsActivity displays the same set of images in a horizontal ViewPager. When the user selects an image in the MainActivity, that image should transition from its position in the grid to the correct page in the second activity's ViewPager.

The problem we want to solve is "what if the user switches pages inside the DetailsActivity"? If this happens, we want to change the shared image that will be used during the return transition. By default, the activity transition framework will use the shared element that was used during the enter transition... but the view pager's page has changed so obviously we want to somehow override this behavior. To do so, we will need to set a SharedElementCallback in your MainActivity and DetailsActivity's onCreate() methods and override the onMapSharedElements() method like so:

private final SharedElementCallback mCallback = new SharedElementCallback() {
    @Override
    public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
        if (mCurrentImagePosition != mOriginalImagePosition) {
            View sharedView = getImageAtPosition(mCurrentImagePosition);
            names.clear();
            sharedElements.clear();
            names.add(sharedView.getTransitionName());
            sharedElements.put(sharedView.getTransitionName(), sharedView);
        }
    }

    /**
     * Returns the image of interest to be used as the entering/returning
     * shared element during the activity transition.
     */
    private View getImageAtPosition(int position) {
        // ...
    }
};

For a more complete solution, I have created a sample project on GitHub that will achieve this effect (there is too much code to post in a single StackOverflow answer).

Solution 2

I'm assuming that you're using Fragment Transitions and that you're not animating between Activities. It is possible in both Activity Transitions and Fragment Transitions, but the specific code is a little different. The postpone is not available on Fragment Transitions, though.

The addSharedElement takes a second parameter. If you know the specific name in the target fragment when you call it, you can just choose the name in the details fragment to use. If you don't, you can choose any name you want and remap it with the SharedElementCallback set on the Fragment.

fragment.setEnterSharedElementCallback(new SharedElementCallback() {
    @Override
    public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
        sharedElements.put("detailView", mTargetDetailView);
    }
});

If it doesn't match on the way back, the same call is made to remap it. This is likely going to be both in the calling fragment and the called fragment -- on the calling fragment, you use setExitSharedElementCallback. I expect this is the case in your app.

Likewise, in Activity Transitions, you set the same SharedElementCallback and use the same mapping, but you do it on the Activity instead.

Share:
11,029

Related videos on Youtube

afollestad
Author by

afollestad

https://af.codes

Updated on July 10, 2020

Comments

  • afollestad
    afollestad almost 4 years

    An app I'm developing displays a grid of images. When you tap an image, it goes into the details view. The details view contains a ViewPager that allows you swipe between every image in the grid. This is done by passing a lists of paths (containing every image in the grid), along with an offset of the image that was tapped so the ViewPager can be set to show that page initially.

    What's the best way to have a shared element transition inside the Fragment of the current offset page in the ViewPager? The grid (RecyclerView) image should expand into the full screen image in the current page. I saw the ability to postpone and resume activity transitions, so the app would wait to display the shared element transition until the image is loaded from the disk. But I want to be able to make it animate to the correct page in the view pager, and also exit to whatever the current page is when the user goes back (since you can swipe between pages). If you swipe to a different page now, the initial page is what animates back into the grid.

    Currently, I assign every image in the Fragments of the view pager with a transitionName in the format "image_[index]". When I start the details activity, I use the same transitionName with the index being the offset.

    Related to this, I was also wondering how to make ripples work with long presses. When you change a view's activated state, it seems to cancel the ripple. I want an effect similar to Gmail, where the ripple starts over again and quickly finishes after a long press is complete and triggers the activated state.

    • Alex Lockwood
      Alex Lockwood over 9 years
      You might want to ask your second question about the ripple in a separate post. Also, I can give a more indepth answer to your question about the view pager and shared elements tomorrow when I have more time, but for now you can check out this sample project. I wrote it a few weeks back and it sounds like it does almost exactly what you are describing. :P
    • afollestad
      afollestad over 9 years
      @AlexLockwood good point. And wow, you're right, I just ran it and it's pretty much exactly what I need; I'll have to look at it over the next few days, thanks!
    • Harry Sharma
      Harry Sharma over 8 years
      Hello this is not working for me.. what i am trying is that i have a list in a viewpager's fragment and on click of any item i want to transact the image to its detail fragment.. i tried every possible solution i got here..but nothing is working..any help will be appreciated.thanks in advance. –
  • afollestad
    afollestad over 9 years
    This is definitely progress towards what I want to do, the problem is that there's two activities. One displays Fragments that contains grids of images. The other contains a ViewPager that has the detail Fragments in it.
  • George Mount
    George Mount over 9 years
    You are using Activity transitions and it just happens that your views are in fragments. You will likely either have to postpone the enter transition or execute the pending fragment transaction in onCreate. What problems are you seeing now?
  • Harry Sharma
    Harry Sharma over 8 years
    Hello this is not working for me.. what i am trying is that i have a list in a viewpager's fragment and on click of any item i want to transact the image to its detail fragment.. i tried every possible solution i got here..but nothing is working..any help will be appreciated.thanks in advance. –
  • schwertfisch
    schwertfisch about 8 years
    Thanks, very useful!
  • zys
    zys over 7 years
    Hello,my demo is very similar to yours in your github,the diff is only a imageview in the viewpager of the second Activity, and i just want to share the image to the first ImageView.my problem is that the first ImageView cant load the image when i get the second Activity,if i cancel the share element animation, it will load @Alex Lockwood