FragmentTransaction анимации для слайдов в за верхней


Я пытаюсь достичь следующего эффекта с помощью FragmentTransaction.setCustomAnimations.

  1. фрагмент показывает
  2. заменить фрагмент A на фрагмент B. фрагмент A должен оставаться видимым во время замены. Фрагмент B должен скользить справа. Фрагмент B должны скользить по верхней части фрагмент А.

у меня нет проблем с получением слайда в настройках анимации. Моя проблема в том, что я не могу понять, как сделать Фрагмент A остается там, где он есть, и находится под фрагментом B, пока работает слайд в анимации. Независимо от того, что я делаю, кажется, что фрагмент A находится сверху.

как я могу добиться этого?

вот код FragmentTransaction:

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.slide_in_right, R.anim.nothing, R.anim.nothing,
    R.anim.slide_out_right);
ft.replace(R.id.fragment_content, fragment, name);
ft.addToBackStack(name);
ft.commit();

как вы можете видеть, я определил анимацию R. anim.ничего для анимации "out", потому что я на самом деле не хочу, чтобы фрагмент A делал что-либо, кроме как просто оставался там, где он находится во время транзакции.

здесь анимационные ресурсы:

slide_in_right.xml

<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_mediumAnimTime"
    android:fromXDelta="100%p"
    android:toXDelta="0"
    android:zAdjustment="top" />

ничего.xml

<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_mediumAnimTime"
    android:fromAlpha="1.0"
    android:toAlpha="1.0"
    android:zAdjustment="bottom" />
7 56

7 ответов:

Я не знаю, если вам все еще нужен ответ, но я недавно должен был сделать то же самое, и я нашел способ сделать то, что вы хотите.

Я сделал что-то вроде этого :

FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();

MyFragment next = getMyFragment();

ft.add(R.id.MyLayout,next);
ft.setCustomAnimations(R.anim.slide_in_right,0);
ft.show(next);
ft.commit();

Я показываю свой фрагмент в FrameLayout.

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

ft.remove(myolderFrag);

не отображается в анимации.

slide_in_right.xml

    <?xml version="1.0" encoding="utf-8"?> 
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate android:duration="150" android:fromXDelta="100%p" 
android:interpolator="@android:anim/linear_interpolator"
android:toXDelta="0" />
 </set>

начиная с Lollipop, вы можете увеличить de translationZ вашего входного фрагмента. Он появится над выходным.

например:

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    ViewCompat.setTranslationZ(getView(), 100.f);
}

Если вы хотите изменить значение translationZ только на время анимации, вы должны сделать что-то вроде этого:

@Override
public Animation onCreateAnimation(int transit, final boolean enter, int nextAnim) {
    Animation nextAnimation = AnimationUtils.loadAnimation(getContext(), nextAnim);
    nextAnimation.setAnimationListener(new Animation.AnimationListener() {

        private float mOldTranslationZ;

        @Override
        public void onAnimationStart(Animation animation) {
            if (getView() != null && enter) {
                mOldTranslationZ = ViewCompat.getTranslationZ(getView());
                ViewCompat.setTranslationZ(getView(), 100.f);
            }
        }

        @Override
        public void onAnimationEnd(Animation animation) {
            if (getView() != null && enter) {
                ViewCompat.setTranslationZ(getView(), mOldTranslationZ);
            }
        }

        @Override
        public void onAnimationRepeat(Animation animation) {
        }
    });
    return nextAnimation;
}

Я нашел решение, которое работает для меня. В итоге я использовал ViewPager с FragmentStatePagerAdapter. ViewPager обеспечивает поведение считывания и fragmentstatepageradapter свопы во фрагментах. Последний трюк для достижения эффекта наличия одной страницы, видимой " под " входящей страницей, заключается в использовании PageTransformer. PageTransformer переопределяет переход ViewPager по умолчанию между страницами. Вот пример PageTransformer, который достигает эффекта с переводом и a небольшое количество накипи на левой стороне страницы.

public class ScalePageTransformer implements PageTransformer {
    private static final float SCALE_FACTOR = 0.95f;

    private final ViewPager mViewPager;

    public ScalePageTransformer(ViewPager viewPager) {
            this.mViewPager = viewPager;
    }

    @SuppressLint("NewApi")
    @Override
    public void transformPage(View page, float position) {
        if (position <= 0) {
            // apply zoom effect and offset translation only for pages to
            // the left
            final float transformValue = Math.abs(Math.abs(position) - 1) * (1.0f - SCALE_FACTOR) + SCALE_FACTOR;
            int pageWidth = mViewPager.getWidth();
            final float translateValue = position * -pageWidth;
            page.setScaleX(transformValue);
            page.setScaleY(transformValue);
            if (translateValue > -pageWidth) {
                page.setTranslationX(translateValue);
            } else {
                page.setTranslationX(0);
            }
        }
    }

}

после дополнительных экспериментов (следовательно, это мой второй ответ), проблема, похоже, заключается в том, что R. anim.ничто не означает "исчезнуть", когда мы хотим другую анимацию, которая явно говорит: "оставайтесь на месте."Решение состоит в том, чтобы определить истинную анимацию" ничего не делать " следующим образом:

файл no_animation.xml:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:interpolator="@android:anim/linear_interpolator"
        android:fromXScale="1.0"
        android:toXScale="1.0"
        android:fromYScale="1.0"
        android:toYScale="1.0"
        android:duration="200"
        />
    <alpha xmlns:android="http://schemas.android.com/apk/res/android"
        android:fromAlpha="1.0"
        android:toAlpha="0.0"
        android:duration="200"
        android:startOffset="200"
        />
</set>

Теперь просто делайте, как вы бы в противном случае:

getActivity().getSupportFragmentManager().beginTransaction()
                .setCustomAnimations(R.anim.slide_in_right, R.anim.no_animation)
                .replace(R.id.container, inFrag, FRAGMENT_TAG)
                .addToBackStack("Some text")
                .commit();

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

final IncomingFragment newFrag = new IncomingFragment();
newFrag.setEnterAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {

                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    clearSelection();
                    inFrag.clearEnterAnimationListener();

                    getFragmentManager().beginTransaction().remove(OutgoingFragment.this).commit();
                }

                @Override
                public void onAnimationRepeat(Animation animation) {

                }
            });

 getActivity().getSupportFragmentManager().beginTransaction()
                    .setCustomAnimations(R.anim.slide_in_from_right, 0)
                    .add(R.id.container, inFrag)
                    .addToBackStack(null)
                    .commit();

это вызывается из внутреннего класса класса OutgoingFragment.

вставляется новый фрагмент, анимация завершается, затем удаляется старый фрагмент.

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

FragmentTransaction ft = ((AppCompatActivity) context).getSupportFragmentManager().beginTransaction();
                ft.setCustomAnimations(0, R.anim.slide_out_to_right);
            if (!fragment.isAdded())
            {
                ft.add(R.id.fragmentContainerFrameMyOrders, fragment);
                ft.show(fragment);
            }
            else
                ft.replace(R.id.fragmentContainerFrameMyOrders, fragment);
            ft.commit();

это мой текущий обходной путь для всех, кто заинтересован.

функции для добавления нового Fragment:

final Fragment toRemove = fragmentManager.findFragmentById(containerID);
if (toRemove != null) {
    new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                fragmentManager.beginTransaction().hide(toRemove).commit();
            }
        }, 
        getResources().getInteger(android.R.integer.config_mediumAnimTime) + 100);
        // Use whatever duration you chose for your animation for this handler
        // I added an extra 100 ms because the first transaction wasn't always 
        // fast enough
}
fragmentManager.beginTransaction()
    .setCustomAnimations(enter, 0, 0, popExit).add(containerID, fragmentToAdd)
    .addToBackStack(tag).commit();

и onCreate:

final FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.addOnBackStackChangedListener(
        new FragmentManager.OnBackStackChangedListener() {
            @Override
            public void onBackStackChanged() {
                Fragment current = fragmentManager.findFragmentById(containerID);
                if (current != null && current.isHidden()) {
                    fragmentManager.beginTransaction().show(current).commit();
                }
            }
        });

я бы предпочел какой-то AnimationListener вместо обработчика выше, но я не видел никакого способа, которым вы могли бы использовать его для обнаружения конца анимации транзакции, которая не была привязана к фрагменту, например onCreateAnimation(). Любые предложения / изменения с соответствующим слушателем будут оцененный.

я укажу на Fragments я добавляю этот способ легкий, поэтому для меня не проблема иметь их в контейнере фрагментов вместе с фрагментом, на котором они находятся.

если вы хотите удалить фрагмент, вы могли бы поставить fragmentManager.beginTransaction().remove(toRemove).commitAllowingStateLoss(); на Handler ' s Runnable и в OnBackStackChangedListener:

// Use back stack entry tag to get the fragment
Fragment current = getCurrentFragment(); 
if (current != null && !current.isAdded()) {
    fragmentManager.beginTransaction()
        .add(containerId, current, current.getTag())
        .commitNowAllowingStateLoss();
}

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

EDIT: Я обнаружил следующие функции это, вероятно, может заменить FragmentTransaction.remove() и FragmentTransaction.add() выше:

FragmentTransaction.отсоединить():

отсоедините данный фрагмент от пользовательского интерфейса. Это то же самое состояние, что и при размещении в обратном стеке: фрагмент удаляется из пользовательского интерфейса, однако его состояние по-прежнему активно управляется диспетчером фрагментов. При переходе в это состояние его иерархия представления разрушенный.

FragmentTransaction.присоединить():

повторно прикрепите фрагмент после того, как он ранее был отсоединен от пользовательского интерфейса с помощью отсоединения(фрагмента). Это приводит к повторному созданию иерархии представлений, присоединению к пользовательскому интерфейсу и отображению.