FragmentTransaction анимации для слайдов в за верхней
Я пытаюсь достичь следующего эффекта с помощью FragmentTransaction.setCustomAnimations.
- фрагмент показывает
- заменить фрагмент 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 ответов:
Я не знаю, если вам все еще нужен ответ, но я недавно должен был сделать то же самое, и я нашел способ сделать то, что вы хотите.
Я сделал что-то вроде этого :
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()
. Любые предложения / изменения с соответствующим слушателем будут оцененный.я укажу на
Fragment
s я добавляю этот способ легкий, поэтому для меня не проблема иметь их в контейнере фрагментов вместе с фрагментом, на котором они находятся.если вы хотите удалить фрагмент, вы могли бы поставить
fragmentManager.beginTransaction().remove(toRemove).commitAllowingStateLoss();
наHandler
' sRunnable
и в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
.присоединить():повторно прикрепите фрагмент после того, как он ранее был отсоединен от пользовательского интерфейса с помощью отсоединения(фрагмента). Это приводит к повторному созданию иерархии представлений, присоединению к пользовательскому интерфейсу и отображению.