Josh
Josh

Reputation: 2725

Reverse shared element transition on back

I am currently using the following code to transition a block on the right side of the screen to a shared element on the left:

 FragmentDetail newFragment = FragmentDetail.newInstance(id);

 setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.trans_move));
 setExitTransition(TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.explode));
 View block = view.findViewById(R.id.blocks);
 block.setTransitionName("block");

 newFragment.setSharedElementEnterTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.trans_move));
 newFragment.setEnterTransition(TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.explode));


 newFragment.setTransitionId(block.getTransitionName());
 FragmentTransaction trans = getFragmentManager().beginTransaction();
 trans.replace(R.id.container, newFragment);
 trans.addToBackStack(null);
 trans.addSharedElement(block, block.getTransitionName());
 trans.commit();

This works exactly how I want, but I would like to reverse the effect upon pressing the back button, animating the item back in. As is, the explode animation plays, but the transition does not.

Any help is greatly appreciated.

Thanks Josh

Upvotes: 14

Views: 6151

Answers (5)

Vodet
Vodet

Reputation: 1519

The Joe Muller answer is correct, I wrote it for Java

@Override
public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    postponeEnterTransition();
    view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            startPostponedEnterTransition();
            view.getViewTreeObserver().removeOnPreDrawListener(this);
            return false;
        }
    });
}

This resolve issue if the start transition is inside an adapter

Upvotes: 0

Code on the Rocks
Code on the Rocks

Reputation: 17606

KOTLIN with Android Navigation Component

For anyone who's here looking for the answer to this question when you're using the Android Navigation component, you can make the reverse transition animation work by adding these lines to the onViewCreated function of the starting fragment:

        postponeEnterTransition()
        view.doOnPreDraw { startPostponedEnterTransition() }

You would generally use this if you are opening the second fragment by clicking on a RecyclerView item.

Upvotes: 8

Sen Yan
Sen Yan

Reputation: 1



I have met the same problem with you.Buy I have found the solution. You know, there are many causes for this problem. I just show my way. Hope that can help you.

there are two fragments. one have a RecyclerView widget:

ListFragment.java

public class ListFragment extends Fragment implements RecyclerItemInter {

@Bind(R.id.recycler_view)
RecyclerView recyclerView;

private OnListItemClickListener onListItemClickListener;

public void setOnListItemClickListener(ListFragment.OnListItemClickListener onListItemClickListener) {
    this.onListItemClickListener = onListItemClickListener;
}

public ListFragment() {
}

public static ListFragment newInstance() {
    ListFragment fragment = new ListFragment();
    return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_list, container, false);
    ButterKnife.bind(this, view);
    recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 2));
    RecyclerAdapter2 adapter = new RecyclerAdapter2(BEAUTY_BEANS);
    recyclerView.setAdapter(adapter);
    adapter.setItemInter(this);
    return view;
}

private static final BeautyBean[] BEAUTY_BEANS = {
        new BeautyBean("Avril Lavigne1", "Avril was born in Canada, the Canadian singer, songwriter creators, actors."),
        new BeautyBean("Avril Lavigne2", "Avril was born in Canada, the Canadian singer, songwriter creators, actors."),
        new BeautyBean("Avril Lavigne3", "Avril was born in Canada, the Canadian singer, songwriter creators, actors."),
        new BeautyBean("Avril Lavigne4", "Avril was born in Canada, the Canadian singer, songwriter creators, actors."),
        new BeautyBean("Avril Lavigne5", "Avril was born in Canada, the Canadian singer, songwriter creators, actors.")
};


@Override
public void onDestroyView() {
    super.onDestroyView();
    ButterKnife.unbind(this);
}

@Override
public void onItemClick(View view, int position) {
}

@Override
public void onIvClick(RecyclerAdapter2.ViewHolder holder, int position) {
    OtherFragment otherFragment = OtherFragment.newInstance();
    otherFragment.setSharedElementEnterTransition(new CustomTransition());
    otherFragment.setSharedElementReturnTransition(new CustomTransition());

    /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        otherFragment.setEnterTransition(new Fade());
        setExitTransition(new Fade());
    }*/

    getActivity().getSupportFragmentManager()
            .beginTransaction()
            .replace(R.id.frame_layout, otherFragment)
            .addToBackStack(null)
            .addSharedElement(holder.getPicIv(), getString(R.string.transition_img))
            .commit();
}

then you should set the TransitionName to every ImageView in the RecyclerView:

@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    if (items[position].getImageId() != 0) {
        holder.getPicIv().setImageResource(items[position].getImageId());
    }
    ViewCompat.setTransitionName(holder.getPicIv(), String.valueOf(position) + "_beauty");

    holder.getTitleTv().setText(items[position].getName());
    holder.getDescTv().setText(items[position].getDesc());
    holder.getLinearLayout().setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (null != itemInter) {
                itemInter.onItemClick(holder.itemView, position);
            }
        }
    });
    holder.getPicIv().setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (null != itemInter) {
                itemInter.onIvClick(holder, position);
            }
        }
    });
}

click the list jump to the OtherFragment.

OtherFragment.java

public class OtherFragment extends Fragment {

public OtherFragment() {
}

public static OtherFragment newInstance() {
    OtherFragment fragment = new OtherFragment();
    return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment_other, container, false);
}

}


fragment_other.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.jacksen.supportlibrarydemo.fragment.OtherFragment">


<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/bg_detail_header"
    android:transitionName="@string/transition_img" />


<android.support.design.widget.FloatingActionButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="end|bottom"
    android:layout_margin="@dimen/fab_margin"
    android:src="@drawable/ic_backup_white_36dp"
    android:transitionName="@string/transition_fab"
    app:borderWidth="0dp"
    app:elevation="5dp"
    app:pressedTranslationZ="10dp"
    app:rippleColor="@color/color_gray" />


the crux of the problem is in this xml. at the beginning, i set the attribute "transitionName" on and its father layout. Actually we don't need to add the attribute on father Layout.

Just add transitionName to what you want to transform.

OK, that is my solution.

Upvotes: 0

Markus Schulz
Markus Schulz

Reputation: 520

switch from

trans.replace(R.id.container, newFragment);

to

trans.hide(oldFragment).add(R.id.container, newFragment).show(newFragment)

and it should work (as in my case). reverting a shared fragment transition seems to only work if you hide the old one, instead of replacing it.

Upvotes: 3

Alex Lockwood
Alex Lockwood

Reputation: 83303

Let's say you have two fragments, A and B, and A commits a fragment transaction to start fragment B.

Then that means the exit and reenter transitions should be set on A and the enter and return transitions should be set on B.

It looks like you are calling setSharedElementReturnTransition on the calling fragment instead of the called fragment (newFragment, in this case), which might be causing the problem.

BTW, you should consider calling the set*****Transition() and setSharedElement*****Transition() methods in your fragment's onCreate() method instead of immediately before a fragment transaction is committed. If a fragment is destroyed and recreated, these transitions will be forgotten... so setting them in onCreate() is much safer.

Upvotes: 4

Related Questions