7
votes

I implemented the shared element transition between from an image in a RecyclerView to an image in a new Fragment. The RecyclerView is hosted in a fragment hosted by an Activity and the target fragment is hosted inside another Activity.

When I click on my image, the transition is happening like expected and I get to my fragment. But when I click the back button, the transition is trying to put my image at the first position of my RecyclerView while it's currently displaying the image which is not necessarily the first one.

From this situation, I thought since it's a list of image in which I set a transition name inside the XML, maybe when going back, since all image has the same transition name, the process is confused and just get the first image with the given name. So maybe I should dynamically give a different transition name to each of them. I also set the same name to the target image and reset it every time a new image is selected. But it's still not working. Can anyone help me with that?

Here is the code inside my onItemClickListener:

mAdapter.setOnItemClickListener(new RssItemAdapter.OnItemCLickListener(){
    @Override
    public void onItemClick(View view, int position) {
        final RssItem item = mAdapter.getItem(position);
        final RssElementIntent intent = new RssElementIntent(getActivity(), item, position);
        // mRootView is the view of the fragment
        final ImageView image = (ImageView) 
mRootView.findViewById(R.id.rss_element_image);
        image.setTransitionName(getString(R.string.transition_name_rss_feed_picture) + position);
        ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
                        getActivity(), image, RssElementFragment.EXTRA_IMAGE);
        ActivityCompat.startActivity(getActivity(), intent, options.toBundle());
    }
});

Here is the code in the target fragment:

rssImage.setTransitionName(getString(R.string.transition_name_rss_feed_picture) + getArguments().getInt(ARG_ELEMENT_POSITION));
ViewCompat.setTransitionName(rssImage, EXTRA_IMAGE);

And here is the themes.xml :

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/theme_primary</item>
        <item name="colorPrimaryDark">@color/theme_primary_dark</item>
        <item name="android:colorAccent">@color/theme_accent</item>

        <!-- enable window content transitions -->
        <item name="android:windowContentTransitions">true</item>

        <item name="android:windowAllowEnterTransitionOverlap">true</item>
        <item name="android:windowAllowReturnTransitionOverlap">true</item>

        <!-- specify shared element transitions -->
        <item name="android:windowSharedElementEnterTransition">
            @transition/change_image_transform
        </item>
        <item name="android:windowSharedElementExitTransition">
            @transition/change_image_transform
        </item>

    </style>

Edit:

I found my error, I was getting the ImageView from the fragment rootView which was giving me a random ImageView from my RecyclerView, which mean the wrong transition name. So I used the view given by onItemClick and it worked! I also move the setTransitionName() inside the adapter.

mAdapter.setOnItemClickListener(new RssItemAdapter.OnItemCLickListener(){
        @Override
        public void onItemClick(View view, int position) {
            final RssItem item = mAdapter.getItem(position);
            final RssElementIntent intent = new RssElementIntent(getActivity(), item, position);
            // WRONG !
            // final ImageView image = (ImageView) mRootView.findViewById(R.id.rss_element_image);
            // Correct
            final ImageView image = (ImageView) view.findViewById(R.id.rss_element_image);

            ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
                            getActivity(), image, RssElementFragment.EXTRA_IMAGE);
            ActivityCompat.startActivity(getActivity(), intent, options.toBundle());
        }
    });
1
I think appending position to transition name may be troublesome, it works fine without position in my case. Maybe you should try to remove position in the transition name. - hidro
I tried and i still get the same result... - lcw_gg
In your Adapter for the RecyclerView, in onBindViewHolder, set a unique transitionName per row for each possible shared element. Normally, this comes from a unique row ID of some sort, but if you don't have anything like that, you can use the position as long as you don't have reordering. When going "back" the View is searched by transitionName. If you don't have a way to uniquely identify at binding time, you can override onMapSharedElements to give the framework the hints it needs. - George Mount
You might find this sample implementation helpful (as an example): github.com/alexjlockwood/activity-transitions - Alex Lockwood
George Mount, what you told me led me to the solution! I set a unique name to each image in my adapter and while debugging I realized that the ImageView I was passing to the ActivityOptionsCompat did not have the same transition name as the ImageView I was clicking. I was getting this ImageView from the rootView and somehow, it was returning a random ImageView from the RecyclerView. So instead of getting it from the rootView, I used the view that onItemClick() give and it worked :) - lcw_gg

1 Answers

10
votes

I found my error, I was getting the ImageView from the fragment rootView which was giving me a random ImageView from my RecyclerView, which mean the wrong transition name. So I used the view given by onItemClick and it worked! I also move the setTransitionName() inside the adapter.

mAdapter.setOnItemClickListener(new RssItemAdapter.OnItemCLickListener(){
        @Override
        public void onItemClick(View view, int position) {
            final RssItem item = mAdapter.getItem(position);
            final RssElementIntent intent = new RssElementIntent(getActivity(), item, position);
            // WRONG !
            // final ImageView image = (ImageView) mRootView.findViewById(R.id.rss_element_image);
            // Correct
            final ImageView image = (ImageView) view.findViewById(R.id.rss_element_image);

            ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
                            getActivity(), image, RssElementFragment.EXTRA_IMAGE);
            ActivityCompat.startActivity(getActivity(), intent, options.toBundle());
        }
    });