SetRetainInstance (true) + setCustomAnimations (…) = анимация для каждого изменения ориентации?

Задний план

У меня есть активность с фрагментом, который нужно анимировать при создании, но не при изменении ориентации.

Фрагмент вставляется в макет динамически, так как он является частью активности в стиле навигации.

Проблема

Я хотел избежать повторного создания фрагмента для изменений конфигурации, поэтому я использовал setRetainInstance в фрагменте. Он работает, но по какой-то причине анимация также перезапускается при каждом повороте устройства.

Что я наделал

Я добавил это к фрагменту:

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

И это к деятельности:

  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" /> 

Что я пробовал

  • Я попытался исправить это, используя вместо «add» вместо «replace» вместо «replace». Это не помогло.
  • Я также пытался всегда выполнять замену фрагмента, и если фрагмент уже существует, делайте это без анимации (на том же фрагменте).
  • Если я удалю вызов setRetainInstance, он работает, но я хочу избежать повторного создания фрагмента.

Вопрос

  1. Как я могу решить эту проблему?
  2. Почему я все еще получаю анимацию для добавления фрагмента?
  3. Что происходит, когда меняются другие конфигурации?

Обходной путь №1

Это решение работает в целом, но это наносит вред жизненному циклу, который вы пытались достичь:

  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(); } }); } 

Я все же хотел бы видеть, что можно сделать, потому что это может привести к тому, что вы не хотите встречаться (например, onDetach для фрагмента).

Обходной путь №2

Один из способов решения этой проблемы – не добавлять анимацию через фрагментатор и просто делать это для самого представления в жизненном цикле фрагмента. Вот как это выглядит:

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)); } 

Solutions Collecting From Web of "SetRetainInstance (true) + setCustomAnimations (…) = анимация для каждого изменения ориентации?"

Как насчет переопределения onCreateAnimation() и предотвращения анимации, если фрагмент воссоздается после поворота?

Как показано здесь: Как отключить / избежать пользовательских анимаций фрагментации после вращения экрана


EDIT: Вот пример кода:

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); } 

Этот фрагмент должен быть базовым, чтобы все фрагменты в этой деятельности расширялись.

Снова взгляните на свой код:

 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, PhoneInsertionFragment.TAG).commit(); } 

Вы используете MyFragment.TAG чтобы найти фрагмент, но вы добавляете к транзакции с помощью PhoneInsertionFragment.TAG .

Вот почему вы каждый раз получаете анимацию и фрагмент. Просто используйте один и тот же тег для find и add и все должно быть хорошо.

Почему это происходит, потому что, когда менеджер фрагментов пытается повторно присоединить фрагмент к ориентации, он применяет анимации, которые уже хранятся в транзакции. (Т. Е. Который вы установили при добавлении фрагмента).

Мы решили это путем обработки изменения ориентации в самой деятельности. Если вы хотите изменить дизайн пользовательского интерфейса, присоедините и отсоедините фрагмент в методе onConfigChanges.