Вставьте фрагмент обратно без воспроизведения Pop-Animation - PullRequest
53 голосов
/ 08 февраля 2012

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

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

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

Пример навигацииможет выглядеть так:

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

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

Ответы [ 12 ]

86 голосов
/ 29 июня 2012

Таким образом, 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;
}

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

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

6 голосов
/ 21 мая 2012

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

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

}
6 голосов
/ 17 февраля 2012

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

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

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

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

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

Допустим, это мероприятие 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.

1 голос
/ 26 июня 2016

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

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

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

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

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

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

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

0 голосов
/ 03 мая 2019

У Android теперь есть способ сделать это без обхода ответа @Geoff.

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

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

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), анимация поп больше не будет воспроизводиться.Результаты на самом деле похожи на результат ответа @ Джеффа.

0 голосов
/ 21 февраля 2019

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

for (fragment in supportFragmentManager.fragments) {
    removeFragment(fragment)
}
if (supportFragmentManager.backStackEntryCount > 0) {
    supportFragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}
0 голосов
/ 02 октября 2017

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

@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);
}
0 голосов
/ 05 сентября 2016

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

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

Обычно, когда я работаю с фрагментами, мне нравится иметь 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, который, в свою очередь, может всплыть 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 );
                 }
             });
    }
}
0 голосов
/ 20 марта 2016

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

FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
    fm.popBackStack();
    overridePendingTransition(0, 0);
}
0 голосов
/ 24 ноября 2014

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

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

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

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

...