Search code examples
androidandroid-fragmentsandroid-5.0-lollipop

Android: No shared element transition between RecyclerView and Fragment


I have the following setup: one MainActivity, one AFragment which has a RecyclerView and a BFragment which is used to contain details to elements in AFragment. When the user taps on an item in AFragment, it takes them to BFragment.

The desired effect I want to create is to take the ImageView for the item in AFragment and transition it to BFragment.

The problem is that currently, for all items on the "first page" of the RecyclerView (by "first page" I mean all items that you can see on the screen when you first enter AFragment), the enter transition into BFragment does not work at all. However when returning from BFragment to AFragment, the transition plays normally. For all other items in the RecyclerView (i.e. items not on the "first page"), both the enter and exit transitions play perfectly.

My code

AFragment

public class AFragment extends Fragment {
    ...
    public static class ItemAdapter extends RecyclerView.Adapter<ViewHolder> {
        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            final Item info = items.get(position);
            ViewCompat.setTransitionName(holder.imageView, "shared_image_" + info.getId());

            holder.imageView.setImageDrawable(info.getIcon());
            holder.name.setText(info.getName());
            holder.rootView.setTag(info.getId());
            
            holder.rootView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    BFragment frag = BFragment.newInstance((Integer) v.getTag());
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        frag.setSharedElementEnterTransition(new ComboTransition());
                        frag.setEnterTransition(new Fade());
                        setExitTransition(new Fade());
                        frag.setSharedElementReturnTransition(new ComboTransition());

                        Log.d(TAG, "Name: " + imageView.getTransitionName());
                    }


                    getFragmentManager().beginTransaction()
                        .addSharedElement(imageView, "shared_image")
                        .replace(R.id.content, frag)
                        .addToBackStack(null)
                        .commit();
                }
            });
        }                   
    }
    ...     
}

BFragment

public class BFragment extends Fragment {
    ...
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        ...
        ImageView imageView = (ImageView) findViewById(R.id.image_view);
        ViewCompat.setTransitionName(imageView, "shared_image");
        ...
    }
    ...     
}

Solution

  • Urg. I found the problem, which is unique to my app. The problem is that I forgot I had another copy of the RecyclerView in BFragment (the reason being I wanted users to be able to search all items in AFragment while in BFragment so the UX is a bit better). So what has happening was that when the user exits AFragment and enters BFragment, the recyclerview in BFragment would get initialized and thus all items on the "first page" also get initialized with the same transition names as the items in AFragment (thus the odd behavior with items on the "first page"). Of course this is really bad as now two items have the same transition name.

    I solved the issue by changing the constructor of ItemAdapter to take in a key unique to the creator and prefixing this key to all transition names.