Вот сценарий: активность содержит фрагмент A
, который , в свою очередь , использует getChildFragmentManager()
для добавления фрагментов A1
и A2
в его onCreate
следующим образом:
getChildFragmentManager()
.beginTransaction()
.replace(R.id.fragmentOneHolder, new FragmentA1())
.replace(R.id.fragmentTwoHolder, new FragmentA2())
.commit()
Пока все хорошо, все работает как положено.
Затем мы запускаем следующую транзакцию в Activity:
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(anim1, anim2, anim1, anim2)
.replace(R.id.fragmentHolder, new FragmentB())
.addToBackStack(null)
.commit()
Во время перехода enter
анимация для фрагмента B
работает правильно, но фрагменты A1 и A2 полностью исчезают . Когда мы отменяем транзакцию с помощью кнопки «Назад», они инициализируются правильно и нормально отображаются во время popEnter
анимации.
В моем кратком тестировании это стало еще более странным - если я установил анимацию для дочерних фрагментов (см. Ниже), exit
анимация запускается с перерывами, когда мы добавляем фрагмент.B
getChildFragmentManager()
.beginTransaction()
.setCustomAnimations(enter, exit)
.replace(R.id.fragmentOneHolder, new FragmentA1())
.replace(R.id.fragmentTwoHolder, new FragmentA2())
.commit()
Эффект, которого я хочу достичь, прост - я хочу exit
(или должна быть popExit
?) Анимацию на фрагменте.A
(anim2) для запуска, анимируя весь контейнер, включая его вложенные дочерние элементы.
Есть ли способ добиться этого?
Edit : Вы можете найти тестовый пример здесь
Edit2 : Спасибо @StevenByle за то, что подтолкнул меня продолжать попытки со статической анимацией. По-видимому, вы можете установить анимацию для каждой операции (не глобально для всей транзакции), что означает, что дочерние элементы могут иметь неопределенный набор статической анимации, в то время как их родитель может иметь другую анимацию, и все это может быть зафиксировано в одной транзакции . См. Обсуждение ниже и обновленный проект тестового примера .
R.id.fragmentHolder
относительно A, A1, A2 и т. Д.?changeFragment
метод только один раз?Ответы:
Чтобы пользователь не видел, как вложенные фрагменты исчезают при удалении / замене родительского фрагмента в транзакции, вы можете «смоделировать» эти фрагменты, которые все еще присутствуют, предоставив их изображение в том виде, в каком они появляются на экране. Это изображение будет использоваться в качестве фона для контейнера вложенных фрагментов, поэтому, даже если представления вложенного фрагмента исчезнут, изображение будет имитировать их присутствие. Кроме того, я не вижу проблемы в потере интерактивности с представлениями вложенных фрагментов, потому что я не думаю, что вы хотите, чтобы пользователь действовал на них, когда они только что удаляются (возможно, как действие пользователя, например хорошо).
Я сделал небольшой пример с настройкой фонового изображения (что-то базовое).
источник
Таким образом, кажется, что для этого существует много разных обходных путей, но, основываясь на ответе @ Jayd16, я думаю, что нашел довольно надежное универсальное решение, которое по-прежнему позволяет настраивать анимацию перехода для дочерних фрагментов и не требует выполнения растровый кеш макета.
Есть
BaseFragment
класс, который расширяетсяFragment
, и сделайте так, чтобы все ваши фрагменты расширяли этот класс (а не только дочерние фрагменты).В этом
BaseFragment
классе добавьте следующее:К сожалению, это требует размышлений; однако, поскольку этот обходной путь предназначен для библиотеки поддержки, вы не рискуете изменить базовую реализацию, если не обновите библиотеку поддержки. Если вы создаете библиотеку поддержки из исходного кода, вы можете добавить аксессор для следующего идентификатора ресурса анимации в
Fragment.java
и устранить необходимость в отражении.Это решение устраняет необходимость «угадывать» продолжительность родительской анимации (так что анимация «ничего не делать» будет иметь ту же продолжительность, что и родительская анимация выхода), и позволяет вам по-прежнему выполнять настраиваемые анимации для дочерних фрагментов (например, если вы ' переставлять дочерние фрагменты местами с разными анимациями).
источник
mNextAnim
теперь внутриmAnimationInfo
объекта. Вы можете получить к нему доступ так:Field animInfoField = Fragment.class.getDeclaredField("mAnimationInfo");
animInfoField.setAccessible(true);
Object animationInfo = animInfoField.get(fragment);
Field nextAnimField = animationInfo.getClass().getDeclaredField("mNextAnim");
val nextAnimResource = nextAnimField.getInt(animationInfo);
заменить строчкуint nextAnimResource = nextAnimField.getInt(fragment);
Я смог придумать довольно чистое решение. ИМО это наименее хакерский, и хотя технически это решение «нарисовать растровое изображение», по крайней мере, оно абстрагируется фрагментом lib.
Убедитесь, что ваши дочерние фраги переопределяют родительский класс следующим образом:
Если у нас есть анимация выхода для дочерних фрагов, они будут анимированы, а не мигать. Мы можем использовать это, имея анимацию, которая просто рисует дочерние фрагменты с полной альфа-версией в течение некоторого времени. Таким образом, они будут оставаться видимыми в родительском фрагменте во время его анимации, обеспечивая желаемое поведение.
Единственная проблема, о которой я могу думать, - это отслеживать эту продолжительность. Возможно, я мог бы установить его на большое число, но я боюсь, что это может иметь проблемы с производительностью, если он все еще где-то рисует эту анимацию.
источник
Я публикую свое решение для ясности. Решение довольно простое. Если вы пытаетесь имитировать анимацию транзакции родительского фрагмента, просто добавьте настраиваемую анимацию в транзакцию дочернего фрагмента с той же продолжительностью. О, и убедитесь, что вы установили пользовательскую анимацию перед add ().
Xml для R.anim.none (время анимации входа / выхода моих родителей составляет 250 мс)
источник
fragmentTransaction.setCustomAnimations(R.anim.none, 0, R.anim.none, R.anim.none)
Я понимаю, что это не может полностью решить вашу проблему, но, возможно, это подойдет кому-то другому, вы можете добавлять
enter
/exit
иpopEnter
/popExit
анимации для своих детей,Fragment
которые на самом деле не перемещают / анимируютFragment
s. Пока анимации имеют ту же продолжительность / смещение, что и их родительскиеFragment
анимации, они будут перемещаться / анимироваться вместе с родительской анимацией.источник
вы можете сделать это в дочернем фрагменте.
источник
@@@@@@@@@@@@@@@@@@@@@@@@@@@@
РЕДАКТИРОВАТЬ: я закончил тем, что не реализовал это решение, поскольку у него были другие проблемы. Square недавно выпустила 2 библиотеки, заменяющие фрагменты. Я бы сказал, что это может быть лучшая альтернатива, чем попытка взломать фрагменты, чтобы сделать что-то, чего Google не хочет, чтобы они делали.
http://corner.squareup.com/2014/01/mortar-and-flow.html
@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Я подумал, что предлагаю это решение, чтобы помочь людям, у которых возникнет эта проблема в будущем. Если вы проследите разговор с другими людьми на исходном плакате и посмотрите на опубликованный им код, вы увидите, что исходный плакат в конечном итоге приходит к выводу об использовании анимации без операции для дочерних фрагментов при анимации родительского фрагмента. Это решение не идеально, поскольку оно заставляет вас отслеживать все дочерние фрагменты, что может быть громоздким при использовании ViewPager с FragmentPagerAdapter.
Поскольку я использую дочерние фрагменты повсюду, я придумал это решение, которое является эффективным и модульным (поэтому его можно легко удалить) на случай, если они когда-либо его исправят, и эта бездействующая анимация больше не нужна.
Есть много способов реализовать это. Я решил использовать синглтон и назвал его ChildFragmentAnimationManager. Он в основном будет отслеживать дочерний фрагмент для меня на основе его родителя и будет применять анимацию без операции к дочерним элементам, когда их спросят.
Затем вам нужен класс, расширяющий фрагмент, который расширяют все ваши фрагменты (по крайней мере, ваши дочерние фрагменты). У меня уже был этот класс, и я назвал его BaseFragment. Когда создается представление фрагментов, мы добавляем его в ChildFragmentAnimationManager и удаляем его при уничтожении. Вы можете сделать это с помощью Attach / Detach или других методов сопоставления в последовательности. Моя логика выбора «Создать / уничтожить представление» заключалась в том, что если у фрагмента нет представления, мне не нужно его анимировать, чтобы его можно было видеть. Этот подход также должен лучше работать с ViewPager, которые используют фрагменты, поскольку вы не будете отслеживать каждый отдельный фрагмент, который удерживает FragmentPagerAdapter, а только 3.
Теперь, когда все ваши фрагменты хранятся в памяти родительским фрагментом, вы можете вызвать для них animate, и ваши дочерние фрагменты не исчезнут.
Кроме того, чтобы он у вас был, вот файл no_anim.xml, который находится в вашей папке res / anim:
Опять же, я не думаю, что это решение идеально, но оно намного лучше, чем для каждого экземпляра, который у вас есть дочерний фрагмент, реализующий собственный код в родительском фрагменте для отслеживания каждого дочернего элемента. Я был там, и это неинтересно.
источник
Я думаю, что нашел лучшее решение этой проблемы, чем создание моментального снимка текущего фрагмента в растровое изображение, как предлагал Luksprog.
Хитрость в том, чтобы скрыть удаляемый или отсоединяемый фрагмент, и только после завершения анимации фрагмент удаляется или отсоединяется в своей собственной транзакции фрагмента.
Представьте, что у нас есть
FragmentA
иFragmentB
с субфрагментами. Теперь, когда вы обычно делаете:Вместо этого вы делаете
Теперь о реализации Фрагмента:
источник
У меня была такая же проблема с фрагментом карты. Он продолжал исчезать во время анимации выхода содержащего его фрагмента. Обходной путь - добавить анимацию для дочернего фрагмента карты, чтобы он оставался видимым во время анимации выхода из родительского фрагмента. Анимация дочернего фрагмента сохраняет альфа-канал на 100% в течение всего периода действия.
Анимация: res / animator / keep_child_fragment.xml
Затем анимация применяется, когда фрагмент карты добавляется к родительскому фрагменту.
Родительский фрагмент
Наконец, продолжительность анимации дочернего фрагмента устанавливается в файле ресурсов.
значения / целые числа.xml
источник
Чтобы анимировать исчезновение обработанных фрагментов, мы можем принудительно вернуть стек обратно в ChildFragmentManager. Это запустит анимацию перехода. Для этого нам нужно поймать событие OnBackButtonPressed или прослушать изменения backstack.
Вот пример с кодом.
источник
Недавно я столкнулся с этой проблемой в своем вопросе: Вложенные фрагменты переходят неправильно
У меня есть решение, которое решает эту проблему без сохранения растрового изображения, использования отражения или каких-либо других неудовлетворительных методов.
Пример проекта можно посмотреть здесь: https://github.com/zafrani/NestedFragmentTransitions.
GIF с эффектом можно посмотреть здесь: https://imgur.com/94AvrW4
В моем примере есть 6 дочерних фрагментов, разделенных между двумя родительскими фрагментами. Я могу без проблем добиться переходов для входа, выхода, всплытия и нажатия. Изменения конфигурации и обратное нажатие также успешно обрабатываются.
Основная часть решения находится в моей функции BaseFragment (фрагмент, расширенный моими дочерними и родительскими фрагментами) onCreateAnimator, который выглядит следующим образом:
Действие и родительский фрагмент отвечают за установку состояний этих логических значений. Легче увидеть, как и где из моего примера проекта.
В своем примере я не использую фрагменты поддержки, но с ними и их функцией onCreateAnimation можно использовать ту же логику.
источник
Простой способ решить эту проблему - использовать
Fragment
класс из этой библиотеки вместо класса фрагмента стандартной библиотеки:https://github.com/marksalpeter/contract-fragment
В качестве примечания, пакет также содержит полезный шаблон делегата
ContractFragment
, который может оказаться полезным для создания ваших приложений, использующих отношения родительско-дочернего фрагмента.источник
Из приведенного выше ответа @kcoppock,
если у вас есть Activity-> Fragment-> Fragments (множественная укладка, помогает следующее), небольшое редактирование лучшего ответа IMHO.
источник
Моя проблема заключалась в удалении родительского фрагмента (ft.remove (фрагмент)), дочерние анимации не выполнялись.
Основная проблема заключается в том, что дочерние фрагменты немедленно УНИЧТОЖАЮТСЯ ДО того, как родительский фрагмент завершил анимацию.
Пользовательские анимации дочерних фрагментов не выполняются при удалении родительского фрагмента
Как и другие ускользают, спрятать РОДИТЕЛЯ (а не ребенка) перед удалением РОДИТЕЛЯ - лучший способ.
Если вы действительно хотите удалить родительский элемент, вам, вероятно, следует настроить прослушиватель для своей пользовательской анимации, чтобы знать, когда анимация закончилась, поэтому вы можете безопасно выполнить некоторую финализацию для родительского фрагмента (удалить). Если вы не сделаете этого своевременно, вы можете убить анимацию. NB анимация выполняется в собственной асинхронной очереди.
Кстати, вам не нужны пользовательские анимации для дочернего фрагмента, поскольку они наследуют родительские анимации.
источник
Проблема устранена в
androidx.fragment:fragment:1.2.0-alpha02
. См. Https://issuetracker.google.com/issues/116675313 для получения дополнительных сведений.источник
Старая ветка, но на случай, если кто-то здесь наткнется:
Все вышеперечисленные подходы кажутся мне очень непривлекательными, решение для растровых изображений очень грязное и неэффективное; другие требуют, чтобы дочерние фрагменты знали о продолжительности перехода, используемой в транзакции, используемой для создания рассматриваемого дочернего фрагмента. Лучшее решение, на мой взгляд, примерно следующее:
Мы просто скрываем текущий фрагмент и добавляем новый фрагмент, по окончании анимации удаляем старый фрагмент. Таким образом, он обрабатывается в одном месте, и растровое изображение не создается.
источник