Intereting Posts
Android: изменение размера изображения в формате XML Как использовать Ontouch и Onclick для ImageButton? PhoneStateListener onMessageWaitingIndicatorChanged () не называется надежно MapFragment.getMapAsync (this) – NullPointerException Как применить стиль ко всем кнопкам приложения для Android Как я могу обновить ADT в Eclipse? Не может получить доступ к памяти по адресу 0X1 после настройки gdb и eclipse для отладки общей библиотеки из приложения Android Как использовать ProGuard в Android Studio? Как использовать команду adb pull? Styling ActionMode ActionBar в Android 5.0 Lollipop (с AppCompat) Квадратная компоновка GridLayoutManager для RecyclerView Почему в последних приложениях нет скриншотов, когда финская активность в firebase onAuthStateChanged Не удается создать и запустить проект тестирования Android, созданный с помощью «ant create test-project», когда тестируемый проект имеет банки в каталоге libs Портер-Дафф: различное поведение для разных форм? Необходим комплексный дизайн интерфейса UI (фрагменты)

Анимация FragmentTransaction для перехода сверху

Я пытаюсь добиться следующего эффекта, используя FragmentTransaction.setCustomAnimations.

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

У меня нет проблем с настройкой слайда в анимации. Моя проблема заключается в том, что я не могу понять, как сделать фрагмент A там, где он есть, и быть UNDER Fragment B во время слайд-анимации. Независимо от того, что я делаю, кажется, что Fragment 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.nothing для анимации «out», потому что на самом деле я не хочу, чтобы Fragment 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" /> 

nothing.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" /> 

Solutions Collecting From Web of "Анимация FragmentTransaction для перехода сверху"

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

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

 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.

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

 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> 

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

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

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

Например:

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

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

Сделать файл 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 я добавляю таким образом, легковесный, поэтому для меня не проблема иметь их в контейнере фрагментов вместе с фрагментом, который они находятся на вершине.

Если вы хотите удалить фрагмент, вы можете поместить fragmentManager.beginTransaction().remove(toRemove).commitAllowingStateLoss(); В Runnable Handler и в 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 . Detach () :

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

FragmentTransaction . Attach () :

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