Воспроизведение фрагмента backstack без воспроизведения Pop-Animation

Я нажимаю фрагмент на стеке фрагментов, используя следующий код:

FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_right, R.anim.slide_in_left, R.anim.slide_out_left); fragmentTransaction.replace(getId(), newFragment); fragmentTransaction.addToBackStack(null); fragmentTransaction.commit(); 

Таким образом, когда стек фрагмента выталкивается, например, при нажатии кнопки «Назад» воспроизводится анимация поп-фрагмента фрагмента. Однако есть ситуации, в которых я хотел бы поместить фрагмент назад, не показывая эту анимацию, например, потому что я только что вернулся из другого действия и хочу отобразить предыдущий фрагмент сразу, без анимации.

Пример навигации может выглядеть следующим образом:

  • Пользователь находится на начальном экране с корневым фрагментом
  • Он выбирает элемент корневого фрагмента, который затем отображает новый фрагмент, чтобы отобразить сведения об этом элементе. Он делает это, используя транзакцию фрагмента, которая устанавливает анимацию как для нажатия, так и для поп-кейса (поэтому, когда пользователь нажимает кнопку «Назад», переход анимируется)
  • Из этого фрагмента начинается действие, которое (по какой-либо причине) удаляет только что показанный объект
  • Когда это действие завершается, я хотел бы вернуться к корневому фрагменту, не показывая «поп-анимацию» фрагмента детали,

Есть ли способ вытащить фрагмент назад без воспроизведения указанной поп-анимации?

Solutions Collecting From Web of "Воспроизведение фрагмента backstack без воспроизведения Pop-Animation"

Итак, Warpzit был на правильном пути, он просто не слишком хорошо разбирался в вашей конкретной проблеме. Я столкнулся с одной и той же проблемой, и вот как я ее решил.

Сначала я создал статическую логическую переменную (для простоты, давайте поместим ее в класс FragmentUtils) …

 public class FragmentUtils { public static boolean sDisableFragmentAnimations = false; } 

Затем, в КАЖДОМ фрагменте, вам нужно переопределить метод onCreateAnimation …

 @Override public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { if (FragmentUtils.sDisableFragmentAnimations) { Animation a = new Animation() {}; a.setDuration(0); return a; } return super.onCreateAnimation(transit, enter, nextAnim); } 

Затем, когда вам нужно очистить заднюю часть от вашей активности, просто выполните следующие действия …

 public void clearBackStack() { FragmentUtils.sDisableFragmentAnimations = true; getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); FragmentUtils.sDisableFragmentAnimations = false; } 

И voila, вызов clearBackStack () вернет вас обратно в корневой фрагмент без каких-либо переходных анимаций.

Надеюсь, что большой G добавит менее глупый способ сделать это в будущем.

Итак, для библиотеки поддержки следующие работы:

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

Вот пример, где я его переопределяю и изменяю заданную продолжительность:

 @Override public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { Animation anim = (Animation) super.onCreateAnimation(transit, enter, nextAnim); if(!enter) { if(anim != null) { anim.setDuration(0); // This doesn't seem to be called. return anim; } else { Animation test = new TestAnimation(); test.setDuration(0); return test; } } return anim; } private class TestAnimation extends Animation { } 

Пользователь находится на начальном экране с корневым фрагментом

Допустим, что корневой фрагмент содержится в Activity A.

Он выбирает элемент корневого фрагмента, который затем отображает новый фрагмент, чтобы отобразить сведения об этом элементе. Он делает это, используя транзакцию фрагмента, которая устанавливает анимацию как для нажатия, так и для поп-кейса (поэтому, когда пользователь нажимает кнопку «Назад», переход анимируется)

Транзакция добавляется в задний стек. Это означает, что при нажатии кнопки «Назад» из фрагмента детали процесс popping анимируется.

Из этого фрагмента начинается действие, которое (по какой-либо причине) удаляет только что показанный объект.

Допустим, что это активность B

Когда это действие завершается, я хотел бы вернуться к корневому фрагменту, не показывая «поп-анимацию» фрагмента детали,

Один из способов получить это поведение – сделать это в Activity B:

 Intent intent = new Intent(this, A.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); finish(); 

Это запустит операцию A, сбросив ее в свое корневое состояние в соответствии с документацией (проверьте последний абзац в разделе, где говорится: «Этот режим запуска также может быть использован для хорошего эффекта в сочетании с FLAG_ACTIVITY_NEW_TASK: ……» )

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

 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); 

Что запускает новую активность без анимации. Если вам нужна анимация, вы можете сделать это, используя метод overridePendingTransition .

Итак, я хотел бы предложить небольшое изменение для ответа Джеффа.

Вместо того, чтобы иметь глобальное статическое логическое значение, я бы предпочел локальный нестатический. Это то, что я придумал.

Создать интерфейс

 public interface TransitionAnimator { void disableTransitionAnimation(); void enableTransitionAnimation(); } 

Сделать фрагмент реализовать этот интерфейс.

 public class MyFragment extends Fragment implements TransitionAnimator { private boolean mTransitionAnimation; @Override public void disableTransitionAnimation() { mTransitionAnimation = false; } @Override public void enableTransitionAnimation() { mTransitionAnimation = true; } @Override public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { Animation result; if (!mTransitionAnimation) { Animation dummyAnimation = new Animation() { }; dummyAnimation.setDuration(0); result = dummyAnimation; } else { result = super.onCreateAnimation(transit, enter, nextAnim); } return result; } 

И тогда, когда вы хотите отключить анимацию перехода для фрагмента, просто выполните

 if (fragment instanceof TransitionAnimator) { ((TransitionAnimator) fragment).disableTransitionAnimation(); } 

Чтобы позволить им, просто сделайте

 if (fragment instanceof TransitionAnimator) { ((TransitionAnimator) fragment).enableTransitionAnimation(); } 

Если вы хотите сделать то же самое для всех фрагментов в менеджере фрагментов, просто выполните

 List<Fragment> fragments = getSupportFragmentManager().getFragments(); for (Fragment fragment : fragments) { if (fragment instanceof TransitionAnimator) { // disable animations ((TransitionAnimator) fragment).disableTransitionAnimation(); } } 

Очень похоже, но без статических полей.

Просто используйте другой перегруженный метод setCustomAnimation() и в котором не устанавливайте R.anim.slide_out, и это решит вашу проблему

Приветствия 🙂

Прежде чем ответить на ваш вопрос, мне нужно задать вопрос сам.

В методе onBackPressed () второго действия вы можете получить доступ к стопке первого действия?

Если да, то вы можете вызвать popBackStackImmediate (String trnaisiotnName, int inclusive), и он удалит переход фрагмента из backstack, и вам не нужно беспокоиться о анимации.

Я предполагаю, что вы можете получить доступ к стопке предыдущей активности, иначе это не будет работать

Это довольно легко достичь с помощью overridePendingTransition(int enterAnim, int exitAnim) с обоими 0 без анимации.

 FragmentManager fm = getSupportFragmentManager(); if (fm.getBackStackEntryCount() > 0) { fm.popBackStack(); overridePendingTransition(0, 0); } 

Это продолжение замечательного ответа Джеффа, но для более динамичного и реального сценария.

Я подумал, что это хорошая записка, но теперь я понимаю, что она немного вышла из-под контроля. Тем не менее, код все там, и я считаю его действительно полезным, хотя он охватывает гораздо больше, чем просто отключить переходные анимации.

Обычно, когда я работаю с Fragments, мне нравится иметь BaseFragment, который прикрепляется к BaseActivityCallback. Этот BaseActivityCallback может использоваться моими фрагментами, чтобы добавить новый фрагмент поверх себя или даже поместить фрагменты под ним, следовательно, желание отключить поп-анимацию – или поп тихо :

 interface BaseActivityCallback { void addFragment ( BaseFragment f, int containerResId ); void popFragment ( boolean silently ); } class BaseActivity extends android.support.v4.app.FragmentActivity implements BaseActivityCallback { public void addFragment ( BaseFragment f, int containerResId ) { FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.setCustomAnimations(R.anim.enter, R.anim.exit, R.anim.enter, R.anim.pop_exit); // http://stackoverflow.com/a/17488542/2412477 ft.addToBackStack(DEFAULT_FRAGMENT_STACK_NAME); ft.replace(containerResId, fragment); ft.commitAllowingStateLoss(); } public void popFragment ( boolean silently ) { FragmentManager fm = getSupportFragmentManager(); if ( silently ) { int count = fm.getFragments().size(); BaseFragment f = (BaseFragment)fm.getFragments().get(count-1); f.setDisableTransitionAnimations(true); } fm.popBackStackImmediate(); } } public abstract class BaseFragment extends android.support.v4.app.Fragment { private static final String TAG = "BaseFragment"; private final String STATE_DISABLE_TRANSITION_ANIMATIONS = TAG+".stateDisableTransitionAnimations"; protected BaseActivityCallback baseActivityCallback; private boolean disableTransitionAnimations; @Override public void onCreate ( @Nullable Bundle savedInstanceState ) { super.onCreate(savedInstanceState); disableTransitionAnimations = (savedInstanceState==null ? false : savedInstanceState.getBoolean(STATE_DISABLE_TRANSITION_ANIMATIONS, false)); } @Override public void onAttach ( Context context ) { super.onAttach(context); baseActivityCallback = (BaseActivityCallback)context; } @Override public void onSaveInstanceState ( Bundle outState ) { super.onSaveInstanceState(outState); outState.putBoolean(STATE_DISABLE_TRANSITION_ANIMATIONS, disableTransitionAnimations); } @Override public Animation onCreateAnimation ( int transit, boolean enter, int nextAnim ) { if ( disableTransitionAnimations ) { Animation nop = new Animation(){}; nop.setDuration(0); return nop; } return super.onCreateAnimation(transit, enter, nextAnim); } public void setDisableTransitionAnimations ( boolean disableTransitionAnimations ) { this.disableTransitionAnimations = disableTransitionAnimations; // http://stackoverflow.com/a/11253987/2412477 } } 

Теперь вы можете создать свою MainActivity и показать, что Fragment1 может добавить еще один Fragment2 который, в свою очередь, может поп -фрагментировать :

 public class MainActivity extends BaseActivity { protected void onCreate ( Bundle savedInstanceState ) { setContentView(R.layout.main_activity); ... if ( getSupportFragmentManager().getFragments() != null && !getSupportFragmentManager().getFragments().isEmpty() ) { addFragment( FragmentA.newInstance(), R.id.main_activity_fragment_container ); } } ... } public class FragmentA extends BaseFragment { public View onCreateView ( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) { ViewGroup root = (ViewGroup)inflater.inflate(R.layout.fragment_a, container, false); ... root.findViewById(R.id.fragment_a_next_button) .setOnClickListener( new View.OnClickListener() { public void onClick ( View v ) { baseActivityCallback.addFragment( FragmentB.newInstance(), R.id.main_activity_fragment_container ); } }); } } public class FragmentB extends BaseFragment { public View onCreateView ( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) { ViewGroup root = (ViewGroup)inflater.inflate(R.layout.fragment_b, container, false); ... root.findViewById(R.id.fragment_b_pop_silently_button) .setOnClickListener( new View.OnClickListener() { public void onClick ( View v ) { baseActivityCallback.popFragment( true ); } }); } } 

Ответ на комментарий Джеффа и plackemacher.

Вы можете попытаться удалить все виды из этого фрагмента. Тогда фрагмент покажет, но он должен быть прозрачным.

Удалить all-1 (я использую навигационный ящик, чтобы фрагмент ящика должен был остаться):

  int size = fragmentsList.size ()-1; FragmentTransaction transaction = fragmentManager.beginTransaction (); transaction.setTransition (FragmentTransaction.TRANSIT_NONE); Fragment fragment; for (int i = size ; i > 0 ; i--) { fragment = fragmentsList.get (i); if(fragment != null) { View viewContainer = fragment.getView (); if (viewContainer != null) { ((ViewGroup) viewContainer).removeAllViews (); } transaction.remove (fragment); } } size = fragmentManager.getBackStackEntryCount (); for (int i = 0; i < size ; i++) { fragmentManager.popBackStack (null, FragmentManager.POP_BACK_STACK_INCLUSIVE); } 

Извините за мой английский