Reverse shared element transition on back
Asked Answered
H

5

14

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

Hinman answered 1/2, 2015 at 13:59 Comment(1)
BTW, you can replace TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.explode) with new Explode().Descartes
A
8

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.

Arborescent answered 24/2, 2020 at 16:54 Comment(1)
it works for me, thanks, man! just voted your two answers.Snow
D
4

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.

Descartes answered 13/3, 2015 at 18:37 Comment(1)
Thanks for the BTW-advice! I was wondering why no transition happened when the fragment was destroyed and recreated. But I did put setSharedElementEnterTransition() in onActivityCreated(), as the TransitionInflater needs an activity or a context, but the setSharedElementEnterTransition() at the previous location was still needed. Did you find a way to put it in onCreate()?Lithometeor
M
3

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.

Mesnalty answered 26/12, 2015 at 21:9 Comment(0)
W
0



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.

Wivina answered 26/4, 2016 at 6:48 Comment(0)
T
0

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

Tull answered 19/8, 2020 at 14:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.