TL; DR: Я ищу полный рабочий образец того, что я буду называть сценарием «трехфрагментной анимации Gmail». В частности, мы хотим начать с двух фрагментов, например:
После некоторого события пользовательского интерфейса (например, касания чего-либо во фрагменте B) мы хотим:
- Фрагмент A, чтобы соскользнуть с экрана влево
- Фрагмент B переместится к левому краю экрана и уменьшится, чтобы занять место, освобожденное фрагментом A
- Фрагмент C, чтобы проскользнуть из правой части экрана и занять место, освобожденное фрагментом B
И при нажатии кнопки BACK мы хотим, чтобы этот набор операций был отменен.
Я видел множество частичных реализаций; Я рассмотрю четыре из них ниже. Помимо того, что они неполные, у всех есть свои проблемы.
@Reto Meier предоставил этот популярный ответ на тот же основной вопрос, указав, что вы будете использовать setCustomAnimations()
с расширением FragmentTransaction
. Для сценария с двумя фрагментами (например, вы сначала видите только фрагмент A и хотите заменить его новым фрагментом B с использованием анимированных эффектов) я полностью согласен. Тем не мение:
- Поскольку вы можете указать только одну «входящую» и одну «выходную» анимацию, я не понимаю, как вы будете обрабатывать все различные анимации, необходимые для сценария с тремя фрагментами.
- В
<objectAnimator>
его образце кода используются жестко привязанные позиции в пикселях, и это может показаться непрактичным с учетом различных размеров экрана, ноsetCustomAnimations()
требует ресурсов анимации, что исключает возможность определения этих вещей в Java. - Я в недоумении, как объект аниматоры для масштаба галстука в таких вещах , как
android:layout_weight
вLinearLayout
выделение пространства на основе процента - Я не понимаю, как фрагмент C обрабатывается с самого начала (
GONE
?android:layout_weight
Из0
? Предварительно анимированных до шкалы 0? Что-то еще?)
@Roman Nurik указывает, что вы можете анимировать любое свойство , включая те, которые вы определяете сами. Это может помочь решить проблему жестко зашитых позиций за счет изобретения собственного подкласса настраиваемого менеджера компоновки. Некоторым это помогает, но остальное решение Reto меня все еще сбивает с толку.
Автор этой записи pastebin показывает некоторый дразнящий псевдокод, в основном говоря, что все три фрагмента изначально будут находиться в контейнере, а фрагмент C будет скрыт с самого начала посредством hide()
операции транзакции. Затем мы show()
C и hide()
A, когда происходит событие пользовательского интерфейса. Однако я не понимаю, как это связано с тем, что B меняет размер. Он также основан на том факте, что вы, по-видимому, можете добавить несколько фрагментов в один и тот же контейнер, и я не уверен, является ли это надежным поведением в долгосрочной перспективе (не говоря уже о том, что оно должно сломаться findFragmentById()
, хотя я могу с этим жить).
Автор этого сообщения в блоге указывает, что Gmail вообще не использует setCustomAnimations()
, а вместо этого напрямую использует аниматоры объектов («вы просто меняете левое поле корневого представления + меняете ширину правого представления»). Тем не менее, это все еще двухфрагментное решение AFAICT, и реализация снова показала жесткие размеры в пикселях.
Я продолжу заниматься этим, так что я, возможно, когда-нибудь сам отвечу на этот вопрос, но я действительно надеюсь, что кто-то разработал решение из трех фрагментов для этого сценария анимации и может опубликовать код (или ссылку на него). Анимация в Android заставляет меня выдергивать волосы, и те из вас, кто видел меня, знают, что это в значительной степени бесплодное занятие.
источник
Ответы:
Загрузил свое предложение на github (работает со всеми версиями Android, хотя для этого вида анимации настоятельно рекомендуется аппаратное ускорение просмотра. Для устройств без аппаратного ускорения реализация кэширования растровых изображений должна подходить лучше)
Демонстрационное видео с анимацией находится здесь (низкая частота кадров, причина трансляции экрана. Фактическая производительность очень высокая)
Использование:
Объяснение :
Создан новый настраиваемый RelativeLayout (ThreeLayout) и 2 настраиваемые анимации (MyScalAnimation, MyTranslateAnimation)
ThreeLayout
получает вес левой панели как param, предполагая, что другой видимый вид имеетweight=1
.Таким образом
new ThreeLayout(context,3)
создается новое представление с тремя дочерними элементами, а левая панель занимает 1/3 всего экрана. Другой вид занимает все доступное пространство.Анимации «Масштабирование и преобразование» фактически изменяют размер и перемещают вид, а не псевдо [масштабировать, перемещать]. Заметьте, что
fillAfter(true)
нигде не используется.View2 правее View1
и
View3 находится справа_of View2
Установив эти правила, RelativeLayout позаботится обо всем остальном. Анимация меняет
margins
(в движении) и[width,height]
масштабЧтобы получить доступ к каждому дочернему элементу (чтобы вы могли надуть его своим фрагментом, вы можете вызвать
Ниже показаны 2 анимации.
Этап 1
Stage2
источник
View
меньшем размере ), но в Gmail / Email мы не получаем меньшие шрифты, только меньше слов.scaleX
значенияView
, хотя я могу неверно истолковывать вещи.Хорошо, вот мое собственное решение, полученное из приложения Email AOSP, согласно предложению @Christopher в комментариях к вопросу.
https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePane
Решение @ weakwire напоминает мое, хотя он использует классику,
Animation
а не аниматоры, и он используетRelativeLayout
правила для принудительного позиционирования. С точки зрения щедрости, он, вероятно, получит награду, если только кто-нибудь еще с хитрым решением еще не отправит ответ.В двух словах,
ThreePaneLayout
в этом проекте естьLinearLayout
подкласс, предназначенный для работы в пейзаже с тремя детьми. Ширина этих дочерних элементов может быть задана в XML-макете любым желаемым способом - я показываю с использованием весов, но у вас может быть определенная ширина, заданная ресурсами измерения или чем-то еще. Третий дочерний элемент - фрагмент C в вопросе - должен иметь нулевую ширину.Однако во время выполнения, независимо от того, как вы изначально настроили ширину, управление шириной берется на себя при
ThreePaneLayout
первом использованииhideLeft()
для переключения с отображения вопроса, называемого фрагментами A и B, на фрагменты B и C.ThreePaneLayout
- который не имеет каких - либо конкретных связей с фрагментами - три части являютсяleft
,middle
иright
. В то время вы звонитеhideLeft()
, мы записываем размерыleft
иmiddle
и обнулять любые весовые коэффициенты , которые были использованы на любом из трех, так что мы можем полностью контролировать размеры. В момент времениhideLeft()
мы устанавливаем размерright
равным исходному размеруmiddle
.Анимации бывают двоякими:
ViewPropertyAnimator
чтобы выполнить перевод трех виджетов слева на ширинуleft
, используя аппаратный уровеньObjectAnimator
настраиваемое псевдо-свойство,middleWidth
чтобы изменитьmiddle
ширину с того, с чего оно начиналось, на исходную ширинуleft
(возможно, лучше использовать
AnimatorSet
иObjectAnimators
для всего этого, хотя пока это работает)(также возможно, что
middleWidth
ObjectAnimator
отрицает значение аппаратного уровня, так как это требует довольно непрерывного признания недействительности)(это , безусловно , возможно , что у меня еще есть пробелы в моей анимации понимания, и что мне нравится вводные заявления)
Конечный эффект заключается в том, что он
left
соскальзывает с экрана,middle
перемещается в исходное положение и размерleft
иright
переводится сразу за нимmiddle
.showLeft()
просто переворачивает процесс с тем же набором аниматоров, только с обратными направлениями.Действие использует a
ThreePaneLayout
для хранения парыListFragment
виджетов и aButton
. Выбор чего-либо в левом фрагменте добавляет (или обновляет содержимое) средний фрагмент. При выборе чего-либо в среднем фрагменте устанавливается заголовокButton
, плюс выполняетсяhideLeft()
наThreePaneLayout
. Нажатие НАЗАД, если мы спрятали левую сторону, выполнитshowLeft()
; в противном случае BACK завершает действие. Поскольку это не используетсяFragmentTransactions
для воздействия на анимацию, мы сами застряли в управлении этим «задним стеком».Связанный выше проект использует собственные фрагменты и собственный фреймворк аниматора. У меня есть другая версия того же проекта, которая использует бэкпорт фрагментов поддержки Android и NineOldAndroids для анимации:
https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePaneBC
Бэкпорт отлично работает на Kindle Fire 1-го поколения, хотя анимация немного дергается, учитывая более низкие характеристики оборудования и отсутствие поддержки аппаратного ускорения. Обе реализации кажутся гладкими на Nexus 7 и других планшетах текущего поколения.
Я, конечно, открыт для идей, как улучшить это решение или другие решения, которые предлагают явные преимущества по сравнению с тем, что я сделал здесь (или тем, что использовал @weakwire).
Еще раз спасибо всем, кто внес свой вклад!
источник
ListView
, быстро нажал кнопку возврата и повторил, тогда весь макет сместился вправо. Он начал показывать столбец с дополнительным пространством слева. Это поведение было введено, потому что я не позволялListView
завершать первую анимацию (начатую нажатием на середину ) и нажимал кнопку «Назад». Я зафиксировал его путем заменыtranslationXBy
сtranslationX
вtranslateWidgets
способе иtranslateWidgets(leftWidth, left, middle, right);
сtranslateWidgets(0, left, middle, right);
вshowLeft()
методе.requestLayout();
наmiddle.requestLayout();
insetMiddleWidth(int value);
method. Это может не повлиять на производительность, так как я предполагаю, что графический процессор все равно выполнит полный проход макета, есть комментарии?Мы создали библиотеку под названием PanesLibrary, которая решает эту проблему. Он даже более гибкий, чем то, что предлагалось ранее, потому что:
Вы можете проверить это здесь: https://github.com/Mapsaurus/Android-PanesLibrary
Вот демонстрация: http://www.youtube.com/watch?v=UA-lAGVXoLU&feature=youtu.be
Это в основном позволяет вам легко добавлять любое количество панелей с динамическим размером и прикреплять фрагменты к этим панелям. Надеюсь, что вы найдете ее полезной! :)
источник
Основываясь на одном из примеров, на которые вы ссылались ( http://android.amberfog.com/?p=758 ), как насчет анимации
layout_weight
свойства, ? Таким образом, вы можете анимировать изменение веса трех фрагментов вместе, и вы получите бонус в том, что все они красиво скользят вместе:Начните с простого макета. Поскольку мы собираемся анимировать
layout_weight
, нам понадобитсяLinearLayout
корневой вид для трех панелей:Затем демонстрационный класс:
Также этот пример не использует FragmentTransactions для достижения анимации (скорее, он анимирует представления, к которым прикреплены фрагменты), поэтому вам нужно будет самостоятельно выполнять все транзакции backstack / fragment, но по сравнению с усилиями по обеспечению работы анимации хорошо, это не похоже на плохой компромисс :)
Ужасное видео с низким разрешением в действии: http://youtu.be/Zm517j3bFCo
источник
TextView
сwrap_content
растяжением себя по вертикали в процессе анимации). Однако может случиться так, что анимация веса - это то, как они изменяют размер того, что я назвал фрагментом B. Спасибо!Здесь не используются фрагменты .... Это настраиваемый макет с 3 дочерними элементами. Когда вы нажимаете на сообщение, вы компенсируете трех детей, используя
offsetLeftAndRight()
и аниматором.В
JellyBean
вы можете включить «Показать макет границ» в настройках «Developper Options». Когда анимация слайда завершена, вы все еще можете видеть, что левое меню все еще существует, но под средней панелью.Это похоже на меню приложения Cyril Mottier Fly-in. , но с 3 элементами вместо 2.
Кроме того,
ViewPager
третий дочерний элемент является еще одним признаком этого поведения:ViewPager
обычно использует фрагменты (я знаю, что они не должны, но я никогда не видел другой реализацииFragment
), и, поскольку вы не можете использоватьFragments
внутри другого,Fragment
3 дочерних элемента наверное не фрагменты ....источник
TranslateAnimation
, хотя я конечно, есть способы использовать аниматоров для достижения тех же целей. Придется провести над этим несколько экспериментов. Спасибо!В настоящее время я пытаюсь сделать что-то подобное, за исключением масштабирования фрагмента B, чтобы занять доступное пространство, и что 3 панели могут быть открыты одновременно, если есть достаточно места. Вот мое решение, но я не уверен, собираюсь ли я его придерживаться. Я надеюсь, что кто-нибудь ответит, показав правильный путь.
Вместо использования LinearLayout и анимации веса я использую RelativeLayout и анимирую поля. Я не уверен, что это лучший способ, потому что он требует вызова requestLayout () при каждом обновлении. Хотя на всех моих устройствах все работает гладко.
Итак, я анимирую макет, я не использую транзакцию фрагментов. Я вручную нажимаю кнопку «Назад», чтобы закрыть фрагмент C, если он открыт.
FragmentB используйте layout_toLeftOf / ToRightOf, чтобы он был выровнен по фрагментам A и C.
Когда мое приложение запускает событие для отображения фрагмента C, я вставляю фрагмент C и выдвигаю фрагмент A одновременно. (2 отдельные анимации). И наоборот, когда открывается фрагмент A, я одновременно закрываю C.
В портретном режиме или на меньшем экране я использую немного другой макет и перемещаю фрагмент C по экрану.
Чтобы использовать процент для ширины фрагментов A и C, я думаю, вам придется вычислить его во время выполнения ... (?)
Вот макет занятия:
Анимация для перемещения FragmentC внутрь или наружу:
источник