Подтвердить что ты не робот

Поп-фрагмент 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();

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

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

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

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

4b9b3361

Ответ 1

Итак, 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);
}

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

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

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

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

Ответ 2

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

В фрагменте, который должен иметь пользовательскую поп-анимацию, вы переопределяете 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 {

}

Ответ 3

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

Допустим, корневой фрагмент содержится в Деятельности А.

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

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

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

Допустим, это деятельность B

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

Один из способов получить это поведение - выполнить это в упражнении 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.

Ответ 4

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

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

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

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

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

Ответ 5

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

Приветствия:)

Ответ 6

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

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

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

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

Ответ 7

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

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

Ответ 8

Это продолжение превосходного ответа на @Geoff, но для более динамичного и реального сценария.

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

Обычно, когда я работаю с 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, который может в свою очередь pop Fragment1:

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

Ответ 9

Отмените это в фрагменте, который вы хотите вывести без анимации, и сохраняйте анимацию при вводе

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

Ответ 10

Более простое решение:

for (fragment in supportFragmentManager.fragments) {
    removeFragment(fragment)
}
if (supportFragmentManager.backStackEntryCount > 0) {
    supportFragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}

Ответ 11

На самом деле, у Android теперь есть способ сделать это без ответа @Geoff.

Чтобы анимация не запускалась в popBackStack(), при накачивании вашего fragment добавьте .setReorderingAllowed(true) к вашему fragmentTransaction .setReorderingAllowed(true).

Так, например:

supportFragmentTransaction.beginTransaction()
    .setReorderingAllowed(true)
    .addToBackStack(null)
    .setCustomAnimations(
        android.R.anim.fade_in,
        android.R.anim.fade_out,
        android.R.anim.fade_in,
        android.R.anim.fade_out
    )
    .replace(yourContainer.id, yourFragment)
    .commit()

Вы заметите, что если вы установите setReorderingAllowed(true), анимация поп больше не будет воспроизводиться. Результаты на самом деле похожи на результат ответа @Geoff.

Ответ 12

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

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

Удалить все-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);
    }

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