Android studio animated background

Содержание
  1. AnimationDrawable ()
  2. Пример с анимацией
  3. Создание анимации в коде программы
  4. Изменяем прозрачность
  5. Анимация при загрузке приложения
  6. Копирование кадров и показ в обратном порядке
  7. Анимация градиентного фона
  8. Анимации в Android по полочкам (Часть 1. Базовые анимации)
  9. Часть 1. Базовые анимации
  10. 1. Кадр за кадром
  11. 2. Анимация свойств объекта (aka Property Animator)
  12. 3. Анимация View (aka View animation)
  13. 4. Анимация векторных ресурсов (aka AnimatedVectorDrawable)
  14. Анимация в Android: плавные переходы фрагментов внутри Bottom Sheet
  15. Бриллиантовый чекаут: предыстория
  16. Смотри, какая классная анимация на iOS. Давай так же сделаем?
  17. В чём, собственно, проблема: где клиенту хорошо, там у нас ограничения
  18. Предварительная разметка
  19. Первые попытки реализовать анимацию
  20. animateLayoutChanges
  21. setCustomAnimations
  22. А может попробуем что-то внезапное: Shared Element Transition
  23. Пишем свой Transition
  24. Реализуем свой Transition за девять шагов
  25. Асинхронная загрузка данных
  26. Всё вместе: что в итоге получилось
  27. А можно было сделать всё иначе?

AnimationDrawable ()

Кадровая (фреймовая) анимация — традиционная анимация при помощи быстрой смены последовательных изображений, как на киноплёнке. Данный вид анимации использует XML-файлы в каталоге res/anim/.

  • графический ресурс для кадра
  • продолжительность кадра

Данная анимация будет выполняться только для трёх кадров. При установке атрибута android:oneshot в true анимация повторится только один раз и после остановки будет содержать последний кадр. Если же атрибут установить в false, то анимация будет циклической. Данный XML-файл, сохраненный в каталоге res/anim проекта, можно добавить как фоновое изображение для компонента и затем запустить анимацию.

Пример с анимацией

Создадим новый проект и добавим на форму две кнопки для управления анимацией.

Для анимации подготовим заранее отобранные файлы, которые необходимо поместить в каталог res/drawable. Наша анимация будет состоять из восьми кадров. Время показа каждого кадра установим в 250 миллисекунд. Запишем наши настройки в XML-файл catanimation.xml в каталоге res/drawable.

Получить объект AnimationDrawable в коде программы можно так:

Управлять объектом AnimationDrawable можно через методы start() и stop().

Запустите приложение и посмотрите на анимацию. Надеюсь, вид кота, делающего упражнения, заставит вас с понедельника начать делать зарядку.

Создание анимации в коде программы

Также можно создавать анимацию в коде — достаточно загрузить последовательно ресурсы кадров и определить время показа для каждого кадра. В качестве примера создадим новый проект, взяв за основу предыдущее упражнение.

В основном классе определим два внутренних метода startFrameAnimation() и stopFrameAnimation(), которые будем вызывать при нажатии кнопок «Пуск» и «Стоп».

В методе startFrameAnimation() реализуем создание анимации. Надо получить кадры анимации в виде набора объектов Drawable, загрузив изображения из ресурсов. Для каждого кадра создается отдельный объект Drawable:

Созданные объекты BitmapDrawable необходимо добавить в объект AnimationDrawable методом addFrame(), который принимает два параметра: кадр анимации (объект Drawable) и продолжительность показа в миллисекундах.

Полный листинг будет следующим:

В методах я также использовал проверку на работающую анимацию (isRunning()), хотя и без этой проверки всё работало. Но мало ли что.

Результат будет тот же самый (теоретически). У меня в эмуляторе почему-то работали только первые три кадра. На форумах многие пишут, что у них выводится только первый кадр. Отсюда можно сделать вывод, что не нужно использовать программную анимацию, а использовать анимацию через XML. Статья писалась в 2012 году, возможно сейчас на эмуляторе таких проблем нет.

Изменяем прозрачность

Класс AnimationDrawable имеет метод setAlpha(), позволяющий изменять прозрачность кадров от 255 до 0. Модифицируем предыдущий пример. Добавим на форму метку и ползунок:

В коде программы добавим слушатель изменения ползунка в методе onCreate():

Анимация при загрузке приложения

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

В этом случае придётся усложнить код. Добавим новый класс:

А в основном классе из первого примера добавим строчку кода:

Теперь при запуске приложения анимация автоматически начнет проигрываться.

Также можно попробовать вызвать метод onWindowFocusChanged().

Ниже есть пример, где код вынесен в onResume().

Копирование кадров и показ в обратном порядке

Мы можем получить отдельные кадры анимации и выстроить новую цепочку. Добавим на экран второй компонент ImageView и запустим в нём анимацию в обратном порядке, скопировав все кадры из первой анимации.

На всякий случай предупрежу, что во всех примерах мы использовали фон: setBackgroundResource(), getBackground() и т.д. Если вы будете использовать атрибут android:src, то соответственно используйте getDrawable() и другие родственные методы.

У AnimationDrawable нет слушателей, позволяющих отслеживать конец анимации. Как вариант, рассчитайте самостоятельно количество кадров и их продолжительность и реализуйте свой способ.

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

Анимация градиентного фона

Создадим ресурсы для цветов в colors.xml.

В папке drawable создайте три файла с именами gradient_1.xml, gradient_2.xml и т.д. Содержимое будет одинаковым, только меняйте имена ресурсов и углы: 0, 255, 45.

В той же папке drawable создайте ещё один файл gradient_animation_list.xml

Присвойте корневому контейнеру идентификатор и созданный ресурс, у меня корневым макетом был FrameLayout.

Источник

Анимации в Android по полочкам (Часть 1. Базовые анимации)

Сегодня я хочу немного рассказать про анимацию в Android. Думаю для всех достаточно очевидный факт, что анимации могут украсить наше приложение. Но я считаю, что они могут намного больше. Первое это — хорошие анимации даже при скромной графике могут вывести наше приложение на абсолютно новый уровень. Второе — это дополнительный инструмент для общения с пользователем, позволяющий фокусировать внимание, подсказывать механики приложения, и многое другое… но это отдельная большая тема.

Сегодня мы поговорим об инструментах для создания анимации. К сожалению, так получилось, что в Android достаточно много способов что либо анимировать, и по началу в них очень легко запутаться. Я постараюсь привести максимально общую классификацию и оставлю ссылки исключительно на документацию, т.к. туториалов можно найти предостаточно. Надеюсь эта статья поможет уложить в голове всё по полочками и, при необходимости создать анимацию, выбрать наиболее подходящий способ.

Читайте также:  Андроид смартфон как плеер

Часть 1. Базовые анимации
Часть 2. Комплексные анимации
Часть 3. «Низкоуровневые» анимации

Часть 1. Базовые анимации

1. Кадр за кадром

Предполагаю, что первая анимация в мире создавалась именно так, и в Android до сих пор доступна эта возможность.

Всё что нужно сделать это создать xml со ссылками на каждый кадр:

И запустить анимацию (Здесь и далее все примеры будут приведены на Kotlin):

Сложные по графике анимации, небольших размеров и подготовленные во внешнем редакторе.

Возможность достичь любой сложности эффектов

Большое потребление ресурсов и, как следствие, довольно затратный импорт в приложение с возможностью получить OutOfMemory. Если по каким-то причинам вам нужно показывать большое количество кадров, то придётся писать свою реализацию с постепенной подгрузкой изображений в память. Но если так пришлось делать, возможно проще прибегнуть к видео?

2. Анимация свойств объекта (aka Property Animator)

Если нам нужно всего-лишь передвинуть что-нибудь на несколько пикселей в сторону или изменить прозрачность, чтобы не плодить миллион очень похожих друг на друга кадров на помощь приходит Animator. Фактически с помощью него можно анимировать любое свойство любых объектов.

Базовый абстрактный класс называется Animator, у него есть несколько наследников, нам важны:

ValueAnimator — позволяет анимировать любое свойство
ObjectAnimator — наследуется от ValueAnimator и имеет упрощённый интерфейс для анимации свойств View.
ViewPropertyAnimator — Предоставляет ещё один удобный интерфейс для анимации View. Не унаследован от Animator и используется в методе View::animate()

Анимацию выше можно описать как в коде:

так и в XML ( animator/open_animator.xml ):

Так-же есть возможность описать нашу анимацию переходов между стейтами View, что соответсвенно, с лёгкостью позволит создать анимированные переходы между стейтами у любых View. Описанная в XML анимация будет автоматически запущена при смене состояния View.
animator/state_animator.xml

Анимация View объектов и любых их параметров
Анимация любых других параметров

В некоторой степени требовательны к ресурсам

3. Анимация View (aka View animation)

До появления Animator в Android были только Animations. Основной недостаток которых был в том что они анимировали только представление вида и никак на самом деле не изменяли его свойства. Поэтому если хочется анимировать перемещение какого-либо элемента, то дополнительно по окончанию анимации нужно изменить ещё его свойства. Такой подход так или иначе не очень удобен, если вам нужна чуть более сложная анимация или нужно отлавливать нажатия в момент анимации.

Анимацию можно запустить как в коде:

так и в XML (обратите внимание, что синтаксис отличается от xml для Animator):
anim/open_animation.xml

Там, где API не позволяет использовать Animator.

Устаревший API, меняет только представление вида.

4. Анимация векторных ресурсов (aka AnimatedVectorDrawable)

На мой взгляд самая интересная часть в Android анимациях. Можно относительно малыми силами добиваться сложных и интересных эффектов. Трансформации иконок в Android сделаны именно так.

VectorDrawable состоит из Path и Group элементов. Создание анимации сводится к тому, чтобы прописать движение к этим элементам. Андроид на картинке выше, в коде будет выглядеть так:

Чтобы не писать XML вручную можно воспользоваться онлайн инструментом.

Начиная с API 25 векторные анимации отрисовываются в RenderThread, поэтому, даже если мы загрузим чем-то наш UI Thread (но мы же никогда так не делаем, да?), анимации всё равно будут проигрываться плавно.

Иконки
Анимационные эффекты

Нет возможности вручную управлять точкой анимации во времени (т.е. фактически отсутствует какой-либо метод, вроде setCurrentTime)

Источник

Анимация в Android: плавные переходы фрагментов внутри Bottom Sheet

Написано огромное количество документации и статей о важной визуальной составляющей приложений — анимации. Несмотря на это мы смогли вляпаться в проблемы столкнулись с загвоздками при её реализации.

Данная статья о проблеме и анализе вариантов её решения. Я не дам вам серебряную пулю против всех монстров, но покажу, как можно изучить конкретного, чтобы создать пулю специально для него. Разберу это на примере того, как мы подружили анимацию смены фрагментов с Bottom Sheet.

Бриллиантовый чекаут: предыстория

Бриллиантовый чекаут — кодовое название нашего проекта. Смысл его очень прост — сократить время, затрачиваемое клиентом на последнем этапе оформления заказа. Если в старой версии для оформления заказа требовалось минимум четыре клика на двух экранах (а каждый новый экран — это потенциальная потеря контекста пользователем), «бриллиантовый чекаут» в идеальном случае требует всего один клик на одном экране.


Сравнение старого и нового чекаута

Между собой мы называем новый экран «шторка». На рисунке вы видите, в каком виде мы получили задание от дизайнеров. Данное дизайнерское решение является стандартным, известно оно под именем Bottom Sheet, описано в Material Design (в том числе для Android) и в разных вариациях используется во многих приложениях. Google предлагает нам два готовых варианта реализации: модальный (Modal) и постоянный (Persistent). Разница между этими подходами описана во многих и многих статьях.

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

Смотри, какая классная анимация на iOS. Давай так же сделаем?

Такой вызов не принять мы не могли! Ладно, шучу по поводу «дизайнеры неожиданно пришли с предложением сделать анимацию», но часть про iOS — чистая правда.

Стандартные переходы между экранами (то есть, отсутствие переходов) выглядели хоть и не слишком коряво, но до соответствия званию «бриллиантовый чекаут» не дотягивали. Хотя, кого я обманываю, это действительно было ужасно:

Что имеем «из коробки»

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

  1. Клиент нажимал на поле адреса пиццерии -> в ответ открывался фрагмент «Самовывоз». Открывался он на весь экран (так было задумано) с резким скачком, при этом список пиццерий появлялся с небольшой задержкой.
  2. Когда клиент нажимал «Назад» -> возврат на предыдущий экран происходил с резким скачком.
  3. При нажатии на поле способа оплаты -> снизу с резким скачком открывался фрагмент «Способ оплаты». Список способов оплаты появлялся с задержкой, при их появлении экран увеличивался со скачком.
  4. При нажатии «Назад» -> возврат обратно с резким скачком.
Читайте также:  Vpn для андроид windows

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

В чём, собственно, проблема: где клиенту хорошо, там у нас ограничения

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

Это привело нас к техническому ограничению: мы решили, что нам нельзя на каждую смену экрана закрывать текущий bottom sheet и показывать новый, а также будет плохо показывать несколько bottom sheet один над другим. Так, в рамках нашей реализации (каждый экран — новый фрагмент), можно сделать только один bottom sheet, который должен двигаться максимально плавно в ответ на действия пользователей.

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

Предварительная разметка

Корневой элемент «шторки» очень простой — это всего лишь прямоугольный фон с закруглёнными сверху углами и контейнер, в который помещаются фрагменты.

И файл dialog_gray200_background.xml выглядит так:

Каждый новый экран представляет собой отдельный фрагмент, фрагменты сменяются с помощью метода replace, тут всё стандартно.

Первые попытки реализовать анимацию

animateLayoutChanges

Вспоминаем о древней эльфийской магии animateLayoutChanges, которая на самом деле представляет собой дефолтный LayoutTransition. Хотя animateLayoutChanges совершенно не рассчитан на смену фрагментов, есть надежда, что это поможет с анимацией высоты. Также FragmentContainerView не поддерживает animateLayoutChanges, поэтому меняем его на старый добрый FrameLayout.

Как видим, изменение высоты контейнера действительно анимируется при смене фрагментов. Переход на экран «Самовывоз» выглядит нормально, но остальное оставляет желать лучшего.

Интуиция подсказывает, что данный путь приведёт к нервно подёргивающемуся глазу дизайнера, поэтому откатываем наши изменения и пробуем что-то другое.

setCustomAnimations

FragmentTransaction позволяет задать анимацию, описанную в xml-формате с помощью метода setCustomAnimation. Для этого в ресурсах создаём папку с названием «anim» и складываем туда четыре файла анимации:

И затем устанавливаем эти анимации в транзакцию:

Получаем вот такой результат:

Что мы имеем при такой реализации:

  • Уже стало лучше — видно как экраны сменяют друг друга в ответ на действие пользователя.
  • Но всё равно есть скачок из-за разной высоты фрагментов. Так происходит из-за того, что при переходе фрагментов в иерархии есть только один фрагмент. Именно он подстраивает высоту контейнера под себя, а второй отображается «как получилось».
  • Всё ещё есть проблема с асинхронной загрузкой данных о способах оплаты — экран появляется сначала пустым, а потом со скачком наполняется контентом.

Это никуда не годится. Вывод: нужно что-то другое.

А может попробуем что-то внезапное: Shared Element Transition

Большинство Android-разработчиков знает про Shared Element Transition. Однако, хотя этот инструмент очень гибкий, многие сталкиваются с проблемами при его использовании и поэтому не очень любят применять его.

Суть его довольно проста — мы можем анимировать переход элементов одного фрагмента в другой. Например, можем элемент на первом фрагменте (назовём его «начальным элементом») с анимацией переместить на место элемента на втором фрагменте (этот элемент назовём «конечным элементом»), при этом с фэйдом скрыть остальные элементы первого фрагмента и с фэйдом показать второй фрагмент. Элемент, который должен анимироваться с одного фрагмента на другой, называется Shared Element.

Чтобы задать Shared Element, нам нужно:

  • пометить начальный элемент и конечный элемент атрибутом transitionName с одинаковым значением;
  • указать sharedElementEnterTransition для второго фрагмента.

А что, если использовать корневую View фрагмента в качестве Shared Element? Возможно Shared Element Transition придумывали не для этого. Хотя если подумать, сложно найти аргумент, почему это решение не подойдёт. Мы хотим анимировать начальный элемент в конечный элемент между двумя фрагментами. Не вижу идеологического противоречия. Давайте попробуем сделать так!

Для каждого фрагмента, который находится внутри «шторки», для корневой View указываем атрибут transitionName с одинаковым значением:

Важно: это будет работать, поскольку мы используем REPLACE в транзакции фрагментов. Если вы используете ADD (или используете ADD и скрываете предыдущий фрагмент с помощью previousFragment.hide() [не надо так делать]), то transitionName придётся задавать динамически и очищать после завершения анимации. Так приходится делать, потому что в один момент времени в текущей иерархии View не может быть две View с одинаковым transitionName. Осуществить это можно, но будет лучше, если вы сможете обойтись без такого хака. Если вам всё-таки очень нужно использовать ADD, вдохновение для реализации можно найти в этой статье.

Далее нужно указать класс Transition’а, который будет отвечать за то, как будет протекать наш переход. Для начала проверим, что есть «из коробки» — используем AutoTransition.

И мы должны задать Shared Element, который хотим анимировать, в транзакции фрагментов. В нашем случае это будет корневая View фрагмента:

Важно: обратите внимание, что transitionName (как и весь Transition API) доступен начиная с версии Android Lollipop.

Посмотрим, что получилось:

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

Раз стандартная реализация нам не подошла, что нужно сделать? Конечно же, нужно переписать всё на Flutter написать свой Transition!

Пишем свой Transition

Transition — это класс из Transition API, который отвечает за создание анимации между двумя сценами (Scene). Основные элементы этого API:

  • Scene — это расположение элементов на экране в определённый момент времени (layout) и ViewGroup, в которой происходит анимация (sceneRoot).
  • Начальная сцена (Start Scene) — это Scene в начальный момент времени.
  • Конечная сцена (End Scene) — это Scene в конечный момент времени.
  • Transition — класс, который собирает свойства начальной и конечной сцены и создаёт аниматор для анимации между ними.
Читайте также:  Сканирование куар кода андроид

В классе Transition мы будем использовать четыре метода:

  • fun getTransitionProperties(): Array. Данный метод должен вернуть набор свойств, которые будут анимироваться. Из этого метода нужно вернуть массив строк (ключей) в свободном виде, главное, чтобы методы captureStartValues и captureEndValues (описанные далее) записали свойства с этими ключами. Пример будет далее.
  • fun captureStartValues(transitionValues: TransitionValues). В данном методе мы получаем нужные свойства layout’а начальной сцены. Например, мы можем получить начальное расположение элементов, высоту, прозрачность и так далее.
  • fun captureEndValues(transitionValues: TransitionValues). Такой же метод, только для получения свойств layout’а конечной сцены.
  • fun createAnimator(sceneRoot: ViewGroup?, startValues: TransitionValues?, endValues: TransitionValues?): Animator?. Этот метод должен использовать свойства начальной и конечной сцены, собранные ранее, чтобы создать анимацию между этими свойствами. Обратите внимание, что если свойства между начальной и конечной сценой не поменялись, то данный метод не вызовется вовсе.

Реализуем свой Transition за девять шагов

    Создаём класс, который представляет Transition.

Напоминаю, что Transition API доступен с версии Android Lollipop.
Реализуем getTransitionProperties.

Поскольку мы хотим анимировать высоту View, заведём константу PROP_HEIGHT, соответствующую этому свойству (значение может быть любым) и вернём массив с этой константой:

Нам нужно запомнить высоту той View, которая хранится в параметре transitionValues. Значение высоты нам нужно записать в поле transitionValues.values (он имеет тип Map) c ключом PROP_HEIGHT:

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

Аналогично предыдущему методу, нужно запомнить высоту View. Но не всё так просто. На предыдущем шаге мы зафиксировали высоту контейнера. Новый фрагмент по высоте может быть меньше, равным или больше предыдущего фрагмента. В первых двух случаях мы можем просто взять высоту нового фрагмента. Однако в случае, когда новый фрагмент должен занять больше места, чем старый, значение высоты будет ограничено высотой контейнера. Поэтому придётся пойти на небольшую хитрость — мы просто измерим view, чтобы определить, сколько места на самом деле ей требуется. Реализация будет выглядеть так:

И метод getViewHeight:

Таким образом, мы знаем начальную и конечную высоту контейнера, и теперь дело за малым — создать анимацию.
Реализация анимации. Fade in.

Начальный фрагмент нам анимировать не нужно, так как при старте транзакции он удалится из иерархии. Будем показывать конечный фрагмент с фэйдом. Добавляем метод в класс «BottomSheetSharedTransition», ничего хитрого:

Реализация анимации. Анимация высоты.

Ранее мы запомнили начальную и конечную высоту, теперь мы можем анимировать высоту контейнера фрагментов:

Создаём ValueAnimator и обновляем высоту конечного фрагмента. Снова ничего сложного, но есть нюанс. Поскольку мы меняем высоту контейнера, после анимации его высота будет фиксированной. Это означает, что если фрагмент в ходе своей работы будет менять высоту, то контейнер не будет подстраиваться под это изменение. Чтобы этого избежать, по окончании анимации нужно установить высоту контейнера в значение WRAP_CONTENT. Таким образом, метод для анимации высоты контейнера будет выглядеть так:

Теперь всего лишь нужно использовать аниматоры, созданные этими функциями.
Реализация анимации. createAnimator.

Всегда анимируем переход.

Последний нюанс касательно реализации данного Transititon’а. Звёзды могут сойтись таким образом, что высота начального фрагмента будет точно равна высоте конечного фрагмента. Такое вполне может быть, если оба фрагмента занимают всю высоту экрана. В таком случае метод «createAnimator» не будет вызван совсем. Что же произойдёт?

  • Не будет Fade’а нового фрагмента, он просто резко появится на экране.
  • Поскольку в методе «captureStartValues» мы зафиксировали высоту контейнера, а анимации не произойдёт, высота контейнера никогда не станет равной WRAP_CONTENT.

Неприятно, но не смертельно. Можно обойти это поведение простым трюком: нужно добавить любое значение, которое будет отличаться для начальной сцены и конечной сцены, в список свойств Transition’а. Можно просто добавить строки с разными значениями:

Обратите внимание, добавилось свойство «PROP_VIEW_TYPE», и в методах «captureStartValues» и «captureEndValues» записываем разные значения этого свойства. Всё, транзишн готов!
Применяем Transition.

Асинхронная загрузка данных

Чтобы анимация началась вовремя и выглядела хорошо, нужно просто отложить переход между фрагментами (и, соответственно, анимацию) до момента, пока данные не будут загружены. Для этого внутри фрагмента нужно вызвать метод postponeEnterTransition. По окончании долгих задач по загрузке данных не забудьте вызвать startPostponedEnterTransition. Я уверен, вы знали об этом приёме, но напомнить лишний раз не помешает.

Всё вместе: что в итоге получилось

С новым BottomSheetSharedTransition и использованием postponeEnterTransition при асинхронной загрузке данных у нас получилась такая анимация:

Когда у нас есть готовый класс Transition’а, его применение сводится к простым шагам:

Шаг 1. При транзакции фрагмента добавляем Shared Element и устанавливаем Transition:

Шаг 2. В разметке фрагментов (текущего фрагмента и следующего), которые должны анимироваться внутри BottomSheetDialogFragment, устанавливаем transitionName:

На этом всё, конец.

А можно было сделать всё иначе?

Всегда есть несколько вариантов решения проблемы. Хочу упомянуть другие возможные подходы, которые мы не попробовали:

  • Отказаться от фрагментов, использовать один фрагмент с множеством View и анимировать конкретные View. Так вы получите больший контроль над анимацией, но потеряете преимущества фрагментов: нативную поддержку навигации и готовую обработку жизненного цикла (придётся реализовывать это самостоятельно).
  • Использовать MotionLayout. Технология MotionLayout на данный момент всё ещё находится на стадии бета, но выглядит очень многообещающе, и уже есть официальные примеры, демонстрирующие красивые переходы между фрагментами.
  • Не использовать анимацию. Да, наш дизайн является частным случаем, и вы вполне можете счесть анимацию в данном случае избыточной. Вместо этого можно показывать один Bottom Sheet поверх другого или скрывать один Bottom Sheet и следом показывать другой.
  • Отказаться от Bottom Sheet совсем. Нет изменения высоты контейнера фрагментов — нет проблем.

Демо проект можно найти вот тут на GitHub. А вакансию Android-разработчика (Нижний Новгород) вот здесь на Хабр Карьера.

Источник

Оцените статью