Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setRetainInstance(true) + setCustomAnimations(...) = animation for each orientation change?

Background

I have an activity with a fragment that needs to be animated when being created, but not when the orientation changes .

The fragment is being inserted into a layout dynamically, as it's a part of a navigation-drawer-style activity.

The problem

I wanted to avoid re-creating the fragment for configuration changes, so I used setRetainInstance in the fragment. It works, but for some reason the animation also restarts each time I rotate the device.

What I've done

I've added this to the fragment:

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

and this to the activity:

    final FragmentManager fragmentManager = getSupportFragmentManager();
    final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    MyFragment fragment= (MyFragment) fragmentManager.findFragmentByTag(MyFragment.TAG);
    if (fragment== null) {
        fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_left, R.anim.slide_out_to_right);
        fragment= new MyFragment();
        fragmentTransaction
                .add(R.id.fragmentContainer, fragment, MyFragment.TAG).commit();
    }

fragment_container.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/fragmentContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

What I've tried

  • I've tried to fix it by using "replace" instead of "add". It didn't help.
  • I've also tried to always perform the replacement of the fragment, and if the fragment is already there, do it without animation (on the same fragment).
  • if I remove the setRetainInstance call, it works, but I want to avoid re-creating the fragment.

Question

  1. How can I solve this issue?
  2. Why do I still get an animation for the adding of the fragment?
  3. What happens when other configurations change?

Workaround #1

This solution works in general, but it causes bad things to the lifecycle you've tried to achieve :

    MyFragment fragment= (MyFragment) fragmentManager.findFragmentByTag(MyFragment.TAG);
    if (MyFragment== null) {
        MyFragment= new MyFragment();
        fragmentManager.beginTransaction().setCustomAnimations(R.anim.slide_in_from_left, R.anim.slide_out_to_right)
                .replace(R.id.fragmentContainer, fragment, MyFragment.TAG).commit();
    } else {
        //workaround: fragment already exists, so avoid re-animating it by quickly removing and re-adding it:
        fragmentManager.beginTransaction().remove(fragment).commit();
        final Fragment finalFragment = fragment;
        new Handler().post(new Runnable() {
            @Override
            public void run() {
                fragmentManager.beginTransaction().replace(R.id.fragmentContainer, fragment, finalFragment .TAG).commit();
            }
        });
    }

I would still want to see what can be done, because this can cause things you didn't want to occur(onDetach for the fragment, for example).

Workaround #2

One way to solve this is to avoid adding the animation via the fragmentManager, and just do it for the view itself within the fragment lifecycle. This is how it looks like:

BaseFragment

@Override
public void onViewCreated(final View rootView, final Bundle savedInstanceState) {
    super.onViewCreated(rootView, savedInstanceState);
    if (savedInstanceState == null)
        rootView.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.slide_in_from_left));
}


@Override
public void onDestroyView() {
    super.onDestroyView();
    if (!getActivity().isChangingConfigurations())
        getView().startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.fade_out));
}
like image 294
android developer Avatar asked Mar 03 '15 12:03

android developer


1 Answers

How about you override the onCreateAnimation() method and prevent the animation from happening if the fragment is being recreated after a rotation?

As shown here: How to disable/avoid Fragment custom animations after screen rotation


EDIT: Here's a sample code:

BaseFragment.java

...
private boolean mNeedToAvoidAnimation;

@Override
public void onDestroyView() {
    super.onDestroyView();
    mNeedToAvoidAnimation = true;
}

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    // This avoids the transaction animation when the orienatation changes
    boolean needToAvoidAnimation = mNeedToAvoidAnimation;
    mNeedToAvoidAnimation = false;
    return needToAvoidAnimation ? new Animation() {
    } : super.onCreateAnimation(transit, enter, nextAnim);
}

This fragment should be the base one that all fragments in this activity will extend.

like image 77
Sebastiano Avatar answered Sep 28 '22 09:09

Sebastiano