Анимация как в приложении YouTube

Когда я стал кастомизировать анимации в своих приложениях, я начал обращать больше внимания на детали, а именно на анимацию в других приложениях от крупных разработчиков. Недавно я заметил анимацию перехода к экрану проигрывания видео в приложении YouTube.
Мне захотелось повторить данную анимацию. Но, так как я даже не представлял, как это можно сделать, я пошел за ответом в Google. Наткнулся на статью про CoordinatorLayout: https://habr.com/ru/post/270121/, в описании была анимация с чем-то похожим на то, что мне нужно. Попробовал - не то. Вернулся в Google.

Спустя некоторое количество запросов наткнулся на статью про MotionLayout: https://habr.com/ru/
company/badoo/blog/458854/
, и после прочтения понял, что это именно то, что мне нужно.
MotionLayout: краткое описание
Данный компонент был анонсирован на презентации Google I/O 2019 как расширение функциональности ConstraintLayout. В документации дается следующее определение:

«MotionLayout — это ConstraintLayout, который позволяет анимировать лэйауты между разными состояниями»

Получается, этот лэйаут умеет всё то, что и ConstraintLayout, и кроме этого позволяет управлять состоянием Transition в реальном времени, опираясь на определенные программные ограничения, а также на действия пользователя. Также про Transition можно почитать в предыдущей моей статье.
От теории к практике
Первым делом необходимо подключить обновленный ConstraintLayout. Если у вас он подключен, то достаточно будет изменить версию:

implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'
Создадим разметку для главного экрана с использованием MotionLayout, добавим контейнер для установки фрагмента, растянем на весь экран:
Прошу обратить внимание на эту строку: app:motionDebug=«SHOW_ALL». Она позволит выводить на экран отладочную информацию, траекторию движения объектов, состояния с началом и концом анимации, а также текущий прогресс. Строчка очень помогает при отладке, но не забудьте удалить её, прежде чем отправлять в продакшн.

Создадим разметку фрагмента, в основе MotionLayout, внутри один ImageView, для наглядности можно поставить туда картинку:
Теперь начинается самое интересное. Надо создать сцену (scene), которая будет описывать анимацию. А так как анимация будет зависеть от действий пользователя (motions), то и называется она соответствующе: MotionScene. Данный элемент будет содержать в себе Transitions, способы взаимодействия (OnSwipe, OnClick), а также наборы для изменения разметки (ConstraintSet). В ресурсы следует добавить директорию xml, именно там будут храниться файлы MotionScene. Создадим новый файл, я назвал его player_scene.xml.
Для удобства использования я создал вторую разметку в свернутом состоянии:
Теперь можно описывать MotionScene.xml:
Как можно заметить, в этом файле повторяются параметры элементов из разметки. Для первого запуска стоит добавить MotionScene и для активити. Оставим его пока без настроек, главное чтобы он был.
Запустим и посмотрим, что получилось.
Очень даже неплохо. Теперь добавим простенький адаптер, чтобы сделать что-то похожее на ленту в YouTube, и посмотрим на результат:
Видно, что recyclerView перехватывает свайп у MotionLayout. Чтобы этого избежать, добавляем recyclerView следующий параметр:

android:nestedScrollingEnabled="false"
Проверяем:
Работает как надо. Теперь добавим BottomNavigationView в активити и Transition, чтобы при открытии фрагмента меню пряталось вниз:
Получается, navigationView должен зависеть от фрагмента, а точнее от его текущего состояния. Для этого используем KeyFrameSet:
Теперь нужно передать текущее состояние фрагмента в активити. Будем использовать TransitionListener:
Получилось немного наоборот. Что ж, в таком случае нужно поменять местами стартовый и финишный transitions. Заодно добавим на взаимодействие некоторые ограничения: в качестве опорного элемента поставим imageView, именно по этому элементу будет определяться направление Transition (если не поставить, то фрагмент будет открываться по свайпу вниз, что немного наоборот), также поставим максимальное отклонение transition от текущей сцены, по которому будет происходить переключение. Это что-то вроде autoComplete.
Помимо этого я решил добавить recyclerView с адаптером на главный экран. Адаптер использовал тот же, только layout немного другой. Запускаем и проверяем.
Теперь всё работает как надо.

Позднее я добавил тулбар с логотипом нашей компании, открытие фрагмента по клику на recycler view item, сворачивание фрагмента по нажатию кнопки "назад", и немного исправил некоторые проблемные моменты. Итоговый результат выглядит следующим образом:
Весь исходный код проекта можно посмотреть на гитхабе: https://github.com/BytePace/
LikeYouTubeTransitionExample


Надеюсь, данная статья была очень полезной!
Для написания этой статьи были использованы следующие источники:

  1. Знакомство с MotionLayout: https://medium.com/google-developers/introduction-to-motionlayout-part-i-29208674b10d
  2. Официальная документация: https://developer.android.com/
    reference/android/support/
    constraint/motion/MotionLayout

  3. А также 2 статьи с Хабра, о которых я написал в самом начале.
Спасибо за внимание!