1. Прежде чем начать
Эта лаборатория кода является частью курса Advanced Android in Kotlin. Вы получите максимальную пользу от этого курса, если будете последовательно проходить лабораторные работы по коду, но это не является обязательным. Все лаборатории кода курса перечислены на целевой странице лабораторий кода Advanced Android в Kotlin .
MotionLayout
— это библиотека, которая позволяет добавлять насыщенное движение в ваше приложение для Android. Он основан на ConstraintLayout,
и позволяет анимировать все, что вы можете создать с помощью ConstraintLayout
.
Вы можете использовать MotionLayout
для анимации местоположения, размера, видимости, альфа-канала, цвета, высоты, вращения и других атрибутов нескольких представлений одновременно. Используя декларативный XML, вы можете создавать скоординированные анимации, включающие несколько представлений, которые трудно реализовать в коде.
Анимации — отличный способ улучшить качество работы приложения. Вы можете использовать анимацию, чтобы:
- Показывать изменения — анимация между состояниями позволяет пользователю естественным образом отслеживать изменения в вашем пользовательском интерфейсе.
- Привлекайте внимание — используйте анимацию, чтобы привлечь внимание к важным элементам пользовательского интерфейса.
- Создавайте красивые дизайны — эффектные движения в дизайне придают приложениям безупречный вид.
Предварительные условия
Эта лаборатория кода предназначена для разработчиков, имеющих некоторый опыт разработки под Android. Прежде чем пытаться завершить эту кодовую работу, вам следует:
- Узнайте, как создать приложение с действием и базовым макетом и запустить его на устройстве или в эмуляторе с помощью Android Studio. Ознакомьтесь с
ConstraintLayout
. Прочтите кодовую лабораторию Constraint Layout , чтобы узнать больше оConstraintLayout
.
Что ты будешь делать
- Определите анимацию с помощью
ConstraintSets
иMotionLayout
- Анимация на основе событий перетаскивания
- Измените анимацию с помощью
KeyPosition
- Изменение атрибутов с помощью
KeyAttribute
- Запуск анимации с помощью кода
- Анимация складных заголовков с помощью
MotionLayout
Что вам понадобится
- Android Studio 4.0 (редактор
MotionLayout
работает только с этой версией Android Studio.)
2. Начало работы
Чтобы загрузить пример приложения, вы можете:
… или клонируйте репозиторий GitHub из командной строки, используя следующую команду:
$ git clone https://github.com/googlecodelabs/motionlayout.git
3. Создание анимации с помощью MotionLayout.
Сначала вы создадите анимацию, которая перемещает вид от верхнего начала экрана к нижнему в ответ на щелчки пользователя.
Чтобы создать анимацию из стартового кода, вам понадобятся следующие основные части:
-
MotionLayout,
который является подклассомConstraintLayout
. Вы указываете все представления для анимации внутри тегаMotionLayout
. -
MotionScene,
— XML-файл, описывающий анимацию дляMotionLayout.
-
Transition,
который является частьюMotionScene
и определяет продолжительность анимации, триггер и способ перемещения представлений. -
ConstraintSet
, определяющий как начальное , так и конечное ограничения перехода.
Давайте рассмотрим каждый из них по очереди, начиная с MotionLayout
.
Шаг 1. Изучите существующий код
MotionLayout
— это подкласс ConstraintLayout
, поэтому он поддерживает все те же функции при добавлении анимации. Чтобы использовать MotionLayout
, вы добавляете представление MotionLayout
, в котором вы бы использовали ConstraintLayout.
- В
res/layout
откройтеactivity_step1.xml.
Здесь у вас естьConstraintLayout
с однимImageView
звезды, внутри которого применен оттенок.
Activity_step1.xml
<!-- initial code -->
<androidx.constraintlayout.widget.ConstraintLayout
...
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/red_star"
...
/>
</androidx.constraintlayout.motion.widget.MotionLayout>
Этот ConstraintLayout
не имеет никаких ограничений, поэтому, если вы запустите приложение сейчас, вы увидите, что звездочки отображаются без ограничений, а это означает, что они будут расположены в неизвестном месте. Android Studio выдаст вам предупреждение об отсутствии ограничений.
Шаг 2. Преобразование в макет движения
Чтобы анимировать с помощью MotionLayout,
вам необходимо преобразовать ConstraintLayout
в MotionLayout
.
Чтобы ваш макет мог использовать сцену движения, он должен указывать на нее.
- Для этого откройте область конструктора. В Android Studio 4.0 вы открываете область дизайна, используя значок разделения или дизайна в правом верхнем углу при просмотре XML-файла макета.
- Открыв область конструктора, щелкните правой кнопкой мыши предварительный просмотр и выберите «Преобразовать в MotionLayout» .
При этом тег ConstraintLayout
заменяется тегом MotionLayout
и добавляется motion:layoutDescription
к тегу MotionLayout
, который указывает на @xml/activity_step1_scene.
Activity_step1**.xml**
<!-- explore motion:layoutDescription="@xml/activity_step1_scene" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:layoutDescription="@xml/activity_step1_scene">
Сцена движения — это отдельный XML-файл, описывающий анимацию в MotionLayout
.
Как только вы преобразуетесь в MotionLayout
, в области дизайна отобразится редактор движения.
В редакторе движений есть три новых элемента пользовательского интерфейса:
- Обзор – это модальный выбор, который позволяет выбирать различные части анимации. На этом изображении выбран
start
ConstraintSet
. Вы также можете выбрать переход междуstart
иend
, щелкнув стрелку между ними. - Раздел . Под обзором находится окно раздела, которое меняется в зависимости от выбранного в данный момент элемента обзора. На этом изображении
start
информацияConstraintSet
отображается в окне выбора. - Атрибут — панель атрибутов отображает и позволяет редактировать атрибуты текущего выбранного элемента либо в окне обзора, либо в окне выбора. На этом изображении показаны атрибуты
start
ConstraintSet
.
Шаг 3. Определите начальные и конечные ограничения.
Все анимации можно определить с точки зрения начала и конца. Начало описывает, как выглядит экран до анимации, а конец описывает, как экран выглядит после завершения анимации. MotionLayout
отвечает за определение того, как анимировать между начальным и конечным состоянием (с течением времени).
MotionScene
использует тег ConstraintSet
для определения начального и конечного состояний. ConstraintSet
— это то, как это звучит: набор ограничений, которые можно применять к представлениям. Сюда входят ограничения ширины, высоты и ConstraintLayout
. Он также включает в себя некоторые атрибуты, такие как alpha
. Он не содержит самих представлений, а только ограничения этих представлений.
Любые ограничения, указанные в ConstraintSet
переопределяют ограничения, указанные в файле макета. Если вы определяете ограничения как в макете, так и в MotionScene
, применяются только ограничения в MotionScene
.
На этом этапе вы ограничите вид звезды так, чтобы он начинался с верхнего начала экрана и заканчивался в нижнем конце экрана.
Вы можете выполнить этот шаг либо с помощью редактора движения, либо напрямую отредактировав текст activity_step1_scene.xml
.
- Выберите
start
ConstraintSet на панели обзора.
- На панели выбора выберите
red_star
. В настоящее время отображается источникlayout
— это означает, что он не ограничен этимConstraintSet
. Используйте значок карандаша в правом верхнем углу, чтобы создать ограничение.
- Убедитесь, что
red_star
отображает источникstart
, когда на панели обзора выбранstart
ConstraintSet
. - На панели Attributes с выбранным
red_star
вstart
ConstraintSet
добавьте Constraint вверху и начните с нажатия синих кнопок + .
- Откройте
xml/activity_step1_scene.xml
чтобы просмотреть код, сгенерированный редактором движения для этого ограничения.
Activity_step1_scene.xml
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
ConstraintSet
имеет id
@id/start
и определяет все ограничения, которые применяются ко всем представлениям в MotionLayout
. Поскольку этот MotionLayout
имеет только одно представление, ему нужен только один Constraint
.
Constraint
внутри ConstraintSet
указывает идентификатор представления, которое оно ограничивает, @id/red_star
определенное в activity_step1.xml
. Важно отметить, что теги Constraint
определяют только ограничения и информацию о макете. Тег Constraint
не знает, что он применяется к ImageView
.
Это ограничение определяет высоту, ширину и два других ограничения, необходимые для ограничения представления red_star
верхним началом его родительского элемента.
- Выберите
end
ConstraintSet на панели обзора.
- Выполните те же действия, что и раньше, чтобы добавить
Constraint
дляred_star
вend
ConstraintSet
. - Чтобы использовать редактор движения для выполнения этого шага, добавьте ограничение
bottom
иend
, нажав синие кнопки + .
- Код в XML выглядит следующим образом:
activitiy_step1_scene.xml
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
Как и @id/start
, этот ConstraintSet
имеет единственное Constraint
для @id/red_star
. На этот раз он ограничивает его нижним краем экрана.
Вам не обязательно называть их @id/start
и @id/end
, но это удобно.
Шаг 4. Определите переход
Каждая MotionScene
также должна включать хотя бы один переход. Переход определяет каждую часть анимации от начала до конца.
Для перехода необходимо указать начальный и конечный ConstraintSet
для перехода. Переход также может указывать, как изменить анимацию другими способами, например, как долго выполнять анимацию или как анимировать путем перетаскивания представлений.
- Редактор движения по умолчанию создал для нас переход при создании файла MotionScene. Откройте
activity_step1_scene.xml
, чтобы увидеть сгенерированный переход.
Activity_step1_scene.xml
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@id/start"
motion:duration="1000">
<KeyFrameSet>
</KeyFrameSet>
</Transition>
Это все, что нужно MotionLayout
для создания анимации. Рассматривая каждый атрибут:
-
constraintSetStart
будет применено к представлениям при запуске анимации. -
constraintSetEnd
будет применено к представлениям в конце анимации. -
duration
указывает, как долго должна длиться анимация в миллисекундах.
Затем MotionLayout
определит путь между начальным и конечным ограничениями и анимирует его в течение указанной продолжительности.
Шаг 5. Предварительный просмотр анимации в редакторе движений.
Анимация: видео воспроизведения предварительного просмотра перехода в редакторе движений.
- Откройте редактор движения и выберите переход, щелкнув стрелку между
start
иend
на панели обзора.
- На панели выбора отображаются элементы управления воспроизведением и полоса прокрутки, когда выбран переход. Нажмите «Воспроизвести» или перетащите текущую позицию, чтобы просмотреть анимацию.
Шаг 6. Добавьте обработчик кликов
Вам нужен способ запустить анимацию. Один из способов сделать это — заставить MotionLayout
реагировать на события щелчка по @id/red_star
.
- Откройте редактор движения и выберите переход, щелкнув стрелку между началом и концом на панели обзора.
- Нажмите Создайте обработчик щелчков или пролистывания на панели инструментов для панели обзора. Это добавляет обработчик, который начнет переход.
- Выберите «Обработчик кликов» во всплывающем окне.
- Измените вид для щелчка на
red_star
.
- Нажмите « Добавить». Обработчик щелчков представлен маленькой точкой в редакторе «Переход в движении».
- Выбрав переход на панели обзора, добавьте атрибут
toggle
clickAction
к обработчику OnClick, который вы только что добавили на панели атрибутов.
- Откройте
activity_step1_scene.xml
, чтобы увидеть код, сгенерированный редактором движений.
Activity_step1_scene.xml
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<!-- MotionLayout will handle clicks on @id/red_star to "toggle" the animation between the start and end -->
<OnClick
motion:targetId="@id/red_star"
motion:clickAction="toggle" />
</Transition>
Transition
сообщает MotionLayout
запускать анимацию в ответ на события щелчка с помощью тега <OnClick>
. Рассматривая каждый атрибут:
-
targetId
— это представление для отслеживания кликов. -
clickAction
toggle
будет переключаться между начальным и конечным состоянием при нажатии. Другие вариантыclickAction
вы можете посмотреть в документации .
- Запустите свой код, нажмите «Шаг 1» , затем нажмите красную звездочку и посмотрите анимацию!
Шаг 5. Анимация в действии
Запустите приложение! Вы должны увидеть, как анимация запускается, когда вы нажимаете на звезду.
Завершенный файл сцены движения определяет один Transition
, который указывает на начальный и конечный ConstraintSet
.
В начале анимации ( @id/start
) значок звездочки ограничен верхней частью экрана. В конце анимации ( @id/end
) значок звездочки ограничивается нижней частью экрана.
<?xml version="1.0" encoding="utf-8"?>
<!-- Describe the animation for activity_step1.xml -->
<MotionScene xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<!-- MotionLayout will handle clicks on @id/star to "toggle" the animation between the start and end -->
<OnClick
motion:targetId="@id/red_star"
motion:clickAction="toggle" />
</Transition>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
</MotionScene>
4. Анимация на основе событий перетаскивания
На этом этапе вы создадите анимацию, которая будет реагировать на событие перетаскивания пользователя (когда пользователь проводит пальцем по экрану) для запуска анимации. MotionLayout
поддерживает отслеживание событий касания для перемещения представлений, а также основанные на физике жесты для плавности движения.
Шаг 1. Проверьте исходный код
- Для начала откройте файл макета
activity_step2.xml
, в котором уже существуетMotionLayout
. Взгляните на код.
Activity_step2.xml
<!-- initial code -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:layoutDescription="@xml/step2" >
<ImageView
android:id="@+id/left_star"
...
/>
<ImageView
android:id="@+id/right_star"
...
/>
<ImageView
android:id="@+id/red_star"
...
/>
<TextView
android:id="@+id/credits"
...
motion:layout_constraintTop_toTopOf="parent"
motion:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.motion.widget.MotionLayout>
Этот макет определяет все виды анимации. Три значка звездочки не ограничены в макете, поскольку они будут анимированы в сцене движения.
К TextView
в титрах применяются ограничения, поскольку он остается в одном и том же месте на протяжении всей анимации и не изменяет никаких атрибутов.
Шаг 2. Анимируйте сцену
Как и в случае с предыдущей анимацией, анимация будет определяться начальным и конечным ConstraintSet,
а также Transition
.
Определите начальный ConstraintSet
- Откройте сцену движения
xml/step2.xml
чтобы определить анимацию. - Добавьте ограничения для начального ограничения
start
. Вначале все три звезды расположены по центру нижней части экрана. Правая и левая звезды имеют значениеalpha
0.0
, что означает, что они полностью прозрачны и скрыты.
шаг2.xml
<!-- TODO apply starting constraints -->
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
<Constraint
android:id="@+id/left_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
<Constraint
android:id="@+id/right_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
В этом ConstraintSet
вы указываете одно Constraint
для каждой звезды. Каждое ограничение будет применено MotionLayout
в начале анимации.
Каждый звездный вид центрируется в нижней части экрана с использованием ограничений начала, конца и низа. Две звезды @id/left_star
и @id/right_star
имеют дополнительное значение альфа, которое делает их невидимыми и которое будет применено в начале анимации.
Наборы ограничений start
и end
определяют начало и конец анимации. Ограничение начала, например motion:layout_constraintStart_toStartOf
, ограничит начало представления началом другого представления. Поначалу это может сбить с толку, поскольку имя start
используется для обоих , и оба они используются в контексте ограничений. Чтобы подчеркнуть различие, start
в layout_constraintStart
относится к «началу» представления, которое находится слева в языке слева направо и справа в языке справа налево. Набор start
ограничений относится к началу анимации.
Определите конечный ConstraintSet
- Определите конечное ограничение, чтобы использовать цепочку для размещения всех трех звезд вместе под
@id/credits
. Кроме того, будет установлено конечное значениеalpha
левой и правой звезд равным1.0
.
шаг2.xml
<!-- TODO apply ending constraints -->
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/left_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1.0"
motion:layout_constraintHorizontal_chainStyle="packed"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toStartOf="@id/red_star"
motion:layout_constraintTop_toBottomOf="@id/credits" />
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toEndOf="@id/left_star"
motion:layout_constraintEnd_toStartOf="@id/right_star"
motion:layout_constraintTop_toBottomOf="@id/credits" />
<Constraint
android:id="@+id/right_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1.0"
motion:layout_constraintStart_toEndOf="@id/red_star"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toBottomOf="@id/credits" />
</ConstraintSet>
Конечным результатом является то, что виды будут распространяться вверх от центра по мере их анимации.
Кроме того, поскольку свойство alpha
установлено для @id/right_start
и @id/left_star
в обоих ConstraintSets
, оба представления будут постепенно проявляться по мере продвижения анимации.
Анимация на основе свайпа пользователя
MotionLayout
может отслеживать события перетаскивания пользователя или пролистывания для создания анимации «броска», основанной на физике. Это означает, что виды будут продолжать работать, если пользователь их бросит, и будут замедляться, как физический объект, катящийся по поверхности. Вы можете добавить этот тип анимации с помощью тега OnSwipe
в Transition
.
- Замените TODO для добавления тега
OnSwipe
на<OnSwipe motion:touchAnchorId="@id/red_star" />
.
шаг2.xml
<!-- TODO add OnSwipe tag -->
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end">
<!-- MotionLayout will track swipes relative to this view -->
<OnSwipe motion:touchAnchorId="@id/red_star" />
</Transition>
OnSwipe
содержит несколько атрибутов, наиболее важным из которых является touchAnchorId
.
-
touchAnchorId
— это отслеживаемое представление, которое перемещается в ответ на прикосновение.MotionLayout
будет держать это представление на том же расстоянии от пальца, который проводит пальцем. -
touchAnchorSide
определяет, какую сторону изображения следует отслеживать. Это важно для представлений, которые меняют размер, следуют по сложным траекториям или имеют одну сторону, которая движется быстрее другой. -
dragDirection
определяет, какое направление имеет значение для этой анимации (вверх, вниз, влево или вправо).
Когда MotionLayout
прослушивает события перетаскивания, прослушиватель будет зарегистрирован в представлении MotionLayout
, а не в представлении, указанном touchAnchorId
. Когда пользователь начинает жест в любом месте экрана, MotionLayout
сохраняет расстояние между пальцем и touchAnchorSide
представления touchAnchorId
постоянным. Например, если они касаются на расстоянии 100 dp от стороны привязки, MotionLayout
будет удерживать эту сторону на расстоянии 100 dp от пальца на протяжении всей анимации.
Попробуйте это
- Запустите приложение еще раз и откройте экран шага 2. Вы увидите анимацию.
- Попробуйте «подбросить» или отпустить палец в середине анимации, чтобы узнать, как
MotionLayout
отображает анимацию, основанную на физике жидкости!
MotionLayout
может анимировать самые разные проекты, используя функции ConstraintLayout
для создания богатых эффектов.
В этой анимации все три представления вначале располагаются относительно родительского элемента внизу экрана. В конце три представления располагаются в цепочке относительно @id/credits
.
Несмотря на столь разные макеты, MotionLayout
создаст плавную анимацию между началом и концом.
5. Изменение пути
На этом этапе вы создадите анимацию, которая следует по сложному пути во время анимации и анимирует титры во время движения. MotionLayout
может изменить путь, по которому будет проходить представление между началом и концом, с помощью KeyPosition
.
Шаг 1. Изучите существующий код
- Откройте
layout/activity_step3.xml
иxml/step3.xml
чтобы увидеть существующий макет и сцену движения.ImageView
иTextView
отображают луну и текст титров. - Откройте файл сцены движения (
xml/step3.xml
). Вы видите, что определенTransition
от@id/start
к@id/end
. Анимация перемещает изображение луны из нижнего левого угла экрана в нижний правый с помощью двухConstraintSets
. Текст титров плавно меняется отalpha="0.0"
доalpha="1.0"
по мере движения луны. - Запустите приложение сейчас и выберите «Шаг 3» . Вы увидите, что луна следует линейному пути (или прямой линии) от начала до конца, когда вы нажмете на луну.
Шаг 2. Включите отладку пути
Прежде чем добавлять дугу к движению Луны, полезно включить отладку пути в MotionLayout
.
Чтобы облегчить разработку сложных анимаций с помощью MotionLayout
, вы можете нарисовать путь анимации каждого представления. Это полезно, когда вы хотите визуализировать анимацию, а также для точной настройки мелких деталей движения.
- Чтобы включить пути отладки, откройте
layout/activity_step3.xml
и добавьтеmotion:motionDebug="SHOW_PATH"
в тегMotionLayout
.
Activity_step3.xml
<!-- Add motion:motionDebug="SHOW_PATH" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:motionDebug="SHOW_PATH" >
После включения отладки пути при повторном запуске приложения вы увидите пути всех представлений, визуализированные пунктирной линией.
- Круги обозначают начальную или конечную позицию одного вида.
- Линии представляют собой путь одного вида.
- Ромбики представляют собой
KeyPosition
, изменяющую путь.
Например, в этой анимации средний круг — это позиция текста титров.
Шаг 3. Измените путь
Все анимации в MotionLayout
определяются начальным и конечным ConstraintSet
, который определяет, как выглядит экран до начала анимации и после ее завершения. По умолчанию MotionLayout
строит линейный путь (прямую линию) между начальной и конечной позицией каждого вида, который меняет положение.
Чтобы построить сложные пути, такие как дуга луны в этом примере, MotionLayout
использует KeyPosition
для изменения пути, который проходит представление между началом и концом.
- Откройте
xml/step3.xml
и добавьте в сценуKeyPosition
. ТегKeyPosition
размещается внутри тегаTransition
.
шаг3.xml
<!-- TODO: Add KeyFrameSet and KeyPosition -->
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
KeyFrameSet
является дочерним элементом Transition
и представляет собой набор всех KeyFrames
, таких как KeyPosition
, которые должны применяться во время перехода.
Поскольку MotionLayout
вычисляет путь для луны между началом и концом, он изменяет путь на основе KeyPosition
, указанного в KeyFrameSet
. Вы можете увидеть, как это изменяет путь, снова запустив приложение.
KeyPosition
имеет несколько атрибутов, которые описывают, как он изменяет путь. Наиболее важные из них:
-
framePosition
— это число от 0 до 100. Оно определяет, когда в анимации следует применять этуKeyPosition
: 1 — это 1% анимации, а 99 — 99% анимации. Поэтому, если значение равно 50, вы применяете его прямо посередине. -
motionTarget
— это представление, для которого этотKeyPosition
изменяет путь. -
keyPositionType
— это то, как этаKeyPosition
изменяет путь. Это может бытьparentRelative
,pathRelative
илиdeltaRelative
(как описано в следующем шаге). -
percentX | percentY
— это степень изменения пути вframePosition
(значения от 0,0 до 1,0, разрешены отрицательные значения и значения > 1).
Вы можете подумать об этом так: «В framePosition
измените путь motionTarget, motionTarget
его на percentX
или percentY
в соответствии с координатами, определенными keyPositionType
».
По умолчанию MotionLayout
скругляет любые углы, возникающие при изменении пути. Если вы посмотрите на только что созданную анимацию, вы увидите, что луна следует по изогнутой траектории на изгибе. Для большинства анимаций это то, что вам нужно, а если нет, вы можете указать атрибут curveFit
чтобы настроить его.
Попробуйте это
Если вы запустите приложение еще раз, вы увидите анимацию этого шага.
Луна следует по дуге, потому что она проходит через KeyPosition
, указанную в Transition
.
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
Вы можете прочитать это KeyPosition
так: «В framePosition 50
(в середине анимации) измените путь motionTarget
@id/moon
переместив его на 50% Y
(на полпути вниз по экрану) в соответствии с координатами, определенными parentRelative
(весь MotionLayout
)"
Итак, в середине анимации луна должна пройти через KeyPosition
, расположенную на 50 % ниже экрана. Это KeyPosition
вообще не изменяет движение X, поэтому луна по-прежнему будет двигаться от начала до конца по горизонтали. MotionLayout
определит плавный путь, проходящий через эту KeyPosition
, перемещаясь между началом и концом.
Если присмотреться, текст титров ограничен положением Луны. Почему он не движется вертикально?
<Constraint
android:id="@id/credits"
...
motion:layout_constraintBottom_toBottomOf="@id/moon"
motion:layout_constraintTop_toTopOf="@id/moon"
/>
Оказывается, даже если вы меняете путь, по которому движется Луна, начальное и конечное положения Луны вообще не перемещают ее по вертикали. KeyPosition
не изменяет начальную или конечную позицию, поэтому текст титров ограничен конечным положением луны.
Если вы хотите, чтобы титры перемещались вместе с луной, вы можете добавить к титрам KeyPosition
или изменить начальные ограничения @id/credits
.
В следующем разделе вы познакомитесь с различными типами keyPositionType
в MotionLayout
.
6. Понимание типа keyPositionType
На последнем шаге вы использовали тип keyPosition
для parentRelative
, чтобы сместить путь на 50% экрана. Атрибут keyPositionType
определяет , как MotionLayout будет изменять путь в соответствии с percentX
или percentY
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
Возможны три различных типа keyPosition
: parentRelative
, pathRelative
и deltaRelative
. Указание типа изменит систему координат, по которой рассчитываются percentX
и percentY
Что такое система координат?
Система координат дает возможность указать точку в пространстве. Они также полезны для описания положения на экране.
Системы координат MotionLayout
представляют собой декартову систему координат . Это означает, что у них есть оси X и Y, определяемые двумя перпендикулярными линиями. Ключевое различие между ними заключается в том, где на экране проходит ось X (ось Y всегда перпендикулярна оси X).
Все системы координат в MotionLayout
используют значения от 0.0
до 1.0
по осям X и Y. Они допускают отрицательные значения и значения больше 1.0
. Так, например, значение percentX
, равное -2.0
будет означать, что нужно дважды пойти в направлении, противоположном оси X.
Если все это слишком похоже на урок алгебры, взгляните на картинки ниже!
родительОтносительные координаты
keyPositionType
parentRelative
использует ту же систему координат, что и экран. Он определяет (0, 0)
в верхнем левом углу всего MotionLayout
и (1, 1)
в правом нижнем углу.
Вы можете parentRelative
всякий раз, когда хотите создать анимацию, которая перемещается по всему MotionLayout
— например, дугу луны в этом примере.
Однако, если вы хотите изменить путь относительно движения, например, сделать его немного изогнутым, лучше выбрать две другие системы координат.
deltaОтносительные координаты
Дельта — это математический термин, обозначающий изменение, поэтому deltaRelative
— это способ сказать «изменение относительное». В координатах deltaRelative
(0,0)
— это начальная позиция представления, а (1,1)
— конечная позиция. Оси X и Y выровнены по экрану.
Ось X всегда горизонтальна на экране, а ось Y всегда вертикальна на экране. По сравнению с parentRelative
основное отличие состоит в том, что координаты описывают только часть экрана, в которой будет перемещаться представление.
deltaRelative
— отличная система координат для изолированного управления горизонтальным или вертикальным движением. Например, вы можете создать анимацию, которая завершает только вертикальное (Y) движение при 50 % и продолжает анимацию по горизонтали (X).
p athОтносительные координаты
Последняя система координат в MotionLayout
— pathRelative
. Он сильно отличается от двух других, поскольку ось X следует за траекторией движения от начала до конца. Итак, (0,0)
— начальная позиция, а (1,0)
— конечная позиция.
Зачем вам это нужно? На первый взгляд это довольно удивительно, тем более что эта система координат даже не выровнена с системой координат экрана.
Оказывается, pathRelative
действительно полезен в нескольких случаях.
- Ускорение, замедление или остановка просмотра во время части анимации. Поскольку измерение X всегда будет точно соответствовать пути, по которому движется представление, вы можете использовать
pathRelative
KeyPosition
чтобы изменить, в какойframePosition
достигается конкретная точка на этом пути. Таким образом,KeyPosition
сframePosition="50"
сpercentX="0.1"
приведет к тому, что анимация займет 50% времени, чтобы пройти первые 10% движения. - Добавление тонкой дуги к пути. Поскольку размер Y всегда перпендикулярен движению, изменение Y изменит траекторию кривой относительно общего движения.
- Добавление второго измерения, когда
deltaRelative
не работает. Для полностью горизонтального и вертикального движенияdeltaRelative
создаст только одно полезное измерение. ОднакоpathRelative
всегда будет создавать пригодные для использования координаты X и Y.
На следующем шаге вы узнаете, как строить еще более сложные пути, используя более одного KeyPosition
.
7. Создание сложных дорожек
Глядя на анимацию, которую вы построили на последнем шаге, она создает плавную кривую, но форма может быть более «луной».
Измените путь с несколькими элементами клавиатуры
MotionLayout
может изменить путь дальше, определив столько KeyPosition
, сколько необходимо, чтобы получить любое движение. Для этой анимации вы построите дугу, но вы можете заставить Луну прыгать вверх и вниз в середине экрана, если хотите.
- Откройте
xml/step4.xml
. Вы видите, что у него есть те же представления иKeyFrame
, который вы добавили на последнем шаге. - Чтобы завершить верхнюю часть кривой, добавьте еще две
KeyPositions
в путь@id/moon
, один раз перед тем, как он достигнет вершины, и одна после.
Step4.xml
<!-- TODO: Add two more KeyPositions to the KeyFrameSet here -->
<KeyPosition
motion:framePosition="25"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
/>
<KeyPosition
motion:framePosition="75"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
/>
Эти KeyPositions
будут применяться на 25% и 75% пути через анимацию и приведут к тому @id/moon
пройдет через путь, который на 60% от верхней части экрана. В сочетании с существующей KeyPosition
на 50%это создает гладкую дугу для следования Луны.
В MotionLayout
вы можете добавить столько KeyPositions
, сколько вам нужно, чтобы получить нужный путь движения. MotionLayout
будет применять каждую KeyPosition
в указанном framePosition
и выяснить, как создать плавное движение, которое проходит все KeyPositions
.
Попробуйте это
- Запустите приложение снова. Перейдите к шагу 4 , чтобы увидеть анимацию в действии. Когда вы нажимаете на Луну, она следует по пути от начала до конца, проходя каждую
KeyPosition
, которая была указана вKeyFrameSet
.
Изучите самостоятельно
Прежде чем перейти к другим типам KeyFrame
, попробуйте добавить еще несколько KeyPositions
в KeyFrameSet
, чтобы увидеть, какие эффекты вы можете создать, просто используя KeyPosition
.
Вот один пример, показывающий, как построить сложный путь, который движется вперед и назад во время анимации.
Step4.xml
<!-- Complex paths example: Dancing moon -->
<KeyFrameSet>
<KeyPosition
motion:framePosition="25"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
motion:percentX="0.1"
/>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
motion:percentX="0.3"
/>
<KeyPosition
motion:framePosition="75"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
motion:percentX="0.1"
/>
</KeyFrameSet>
Как только вы закончите исследовать KeyPosition
, на следующем шаге вы переходите к другим типам KeyFrames
.
8. Изменение атрибутов во время движения
Построение динамической анимации часто означает изменение size
, rotation
или alpha
видов по мере развития анимации. MotionLayout
поддерживает анимирование многих атрибутов в любом представлении, используя KeyAttribute
.
На этом этапе вы будете использовать KeyAttribute
, чтобы сделать масштаб луны и вращаться. Вы также будете использовать KeyAttribute
, чтобы задержать внешний вид текста, пока Луна почти завершит свое путешествие.
Шаг 1: Изменить размер и повернуть с помощью KeyAttribute
- Откройте
xml/step5.xml
, которая содержит ту же анимацию, которую вы построили на последнем шаге. Для разнообразия этот экран использует другое пространство в качестве фона. - Чтобы луна расширилась по размеру и повернуте, добавьте два тега
KeyAttribute
вKeyFrameSet
AtkeyFrame="50"
иkeyFrame="100"
Step5.xml
<!-- TODO: Add KeyAttributes to rotate and resize @id/moon -->
<KeyAttribute
motion:framePosition="50"
motion:motionTarget="@id/moon"
android:scaleY="2.0"
android:scaleX="2.0"
android:rotation="-360"
/>
<KeyAttribute
motion:framePosition="100"
motion:motionTarget="@id/moon"
android:rotation="-720"
/>
Эти KeyAttributes
применяются на 50% и 100% анимации. Первый KeyAttribute
при 50% произойдет в верхней части дуги, и приведет к удвоению вида, а также повернуть -360 градусов (или один полный круг). Второй KeyAttribute
завершит второе вращение до -720 градусов (два полных круга) и уменьшит размер обратно до регулярного, поскольку scaleX
и scaleY
значений по умолчанию до 1,0.
Так же, как KeyPosition
, KeyAttribute
использует framePosition
и motionTarget
чтобы указать, когда применить KeyFrame
и какое представление для изменения. MotionLayout
будет интерполировать между KeyPositions
для создания жидкости анимации.
KeyAttributes
поддерживают атрибуты , которые могут применяться ко всем представлениям. Они поддерживают изменение основных атрибутов, таких как visibility
, alpha
или elevation
. Вы также можете изменить вращение, как вы здесь делаете, повернуть в три измерения с rotateX
и rotateY
, масштабируйте размер с помощью scaleX
и scaleY
, или перевести положение вида в X, Y или Z.
Шаг 2: Задержка появления кредитов
Одна из целей этого шага - обновить анимацию, чтобы текст титров не появлялся до тех пор, пока анимация в основном не будет завершена.
- Чтобы отложить появление кредитов, определите еще один
KeyAttribute
, который гарантирует, чтоalpha
останется 0 доkeyPosition="85"
.MotionLayout
по -прежнему будет плавно переходить от 0 до 100 альфой, но он сделает это за последние 15% анимации.
Step5.xml
<!-- TODO: Add KeyAttribute to delay the appearance of @id/credits -->
<KeyAttribute
motion:framePosition="85"
motion:motionTarget="@id/credits"
android:alpha="0.0"
/>
Этот KeyAttribute
сохраняет alpha
@id/credits
в 0,0 для первых 85% анимации. Поскольку он начинается с альфа -0, это означает, что это будет невидимо для первых 85% анимации.
Конечным эффектом этого KeyAttribute
является то, что кредиты появляются к концу анимации. Это дает появление того, что они координируются с луной, оседающей в правом углу экрана.
Задерживая анимации на одном представлении, в то время как другой вид движется так, вы можете создавать впечатляющие анимации, которые чувствуют себя динамичными для пользователя.
Попробуйте это
- Запустите приложение снова и перейдите к шагу 5 , чтобы увидеть анимацию в действии. Когда вы нажмете на Луну, он будет следовать по пути от начала до конца, проходя через каждый
KeyAttribute
, который был указан вKeyFrameSet
.
Поскольку вы поворачиваете луну два полных круга, теперь она сделает двойную спину, и титры откладывают их внешний вид, пока анимация не закончится.
Изучите самостоятельно
Прежде чем перейти к окончательному типу KeyFrame
, попробуйте изменить другие стандартные атрибуты в KeyAttributes
. Например, попробуйте изменить rotation
на rotationX
, чтобы увидеть, какую анимацию он производит.
Вот список стандартных атрибутов, которые вы можете попробовать:
-
android:visibility
-
android:alpha
-
android:elevation
-
android:rotation
-
android:rotationX
-
android:rotationY
-
android:scaleX
-
android:scaleY
-
android:translationX
-
android:translationY
-
android:translationZ
9. Изменение пользовательских атрибутов
Богатые анимации включают изменение цвета или других атрибутов представления. В то время как MotionLayout
может использовать KeyAttribute
для изменения любого из стандартных атрибутов, перечисленных в предыдущей задаче, вы используете CustomAttribute
для указания любого другого атрибута.
CustomAttribute
можно использовать для установки любого значения, которое имеет сеттер. Например, вы можете установить FounalColor в представлении, используя CustomAttribute
. MotionLayout
будет использовать отражение, чтобы найти сеттер, а затем назовите его неоднократно, чтобы оживить представление.
На этом этапе вы будете использовать CustomAttribute
, чтобы установить атрибут colorFilter
на Луне, чтобы построить анимацию, показанную ниже.
Определите пользовательские атрибуты
- Чтобы начать открыть
xml/step6.xml
, которая содержит ту же анимацию, которую вы построили на последнем шаге. - Чтобы заменить цвета луны, добавьте два
KeyAttribute
сCustomAttribute
вKeyFrameSet
AtkeyFrame="0"
,keyFrame="50"
иkeyFrame="100".
step6.xml
<!-- TODO: Add Custom attributes here -->
<KeyAttribute
motion:framePosition="0"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFFFFF"
/>
</KeyAttribute>
<KeyAttribute
motion:framePosition="50"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFB612"
/>
</KeyAttribute>
<KeyAttribute
motion:framePosition="100"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFFFFF"
/>
</KeyAttribute>
Вы добавляете CustomAttribute
в KeyAttribute
. CustomAttribute
будет применен в framePosition
, указанном в KeyAttribute
.
Внутри CustomAttribute
вы должны указать attributeName
и одно значение для установки.
-
motion:attributeName
- это имя сеттера, который будет вызван этим пользовательским атрибутом. В этом примере будет вызванsetColorFilter
наDrawable
. -
motion:custom*Value
- это пользовательское значение типа, отмеченного в имени, в этом примере пользовательское значение является указанным цветом.
Пользовательские значения могут иметь любой из следующих типов:
- Цвет
- Целое число
- Плавать
- Нить
- Измерение
- логическое значение
Используя этот API, MotionLayout
может оживить все, что обеспечивает сеттер для любого представления.
Попробуйте это
- Запустите приложение снова и перейдите к шагу 6 , чтобы увидеть анимацию в действии. Когда вы нажмете на Луну, он будет следовать по пути от начала до конца, проходя через каждый
KeyAttribute
, который был указан вKeyFrameSet
.
Когда вы добавляете больше KeyFrames
, MotionLayout
изменяет путь Луны с прямой линии на сложную кривую, добавляя двойной обратный флип, изменение размера и изменение цвета в середине анимации.
В реальных анимациях вы часто оживляете несколько просмотров одновременно, контролируя их движение по разным путям и скоростям. Указав свой KeyFrame
для каждого представления, можно хореографировать богатые анимации, которые оживляют несколько представлений с помощью MotionLayout
.
10. События перетаскивания и сложные пути
На этом этапе вы исследуете, используя OnSwipe
со сложными путями. До сих пор анимация Луны была вызвана прослушивателем OnClick
и работает на фиксированную продолжительность.
Контроль анимаций, которые имеют сложные пути, используя OnSwipe
, например, анимация Moon, которую вы создали в последних нескольких шагах, требует понимания того, как работает OnSwipe
.
Шаг 1: Исследуйте поведение в сфере
- Откройте
xml/step7.xml
и найдите существующее объявлениеOnSwipe
.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
/>
- Запустите приложение на своем устройстве и перейдите к шагу 7 . Посмотрите, сможете ли вы создать плавную анимацию, перетаскивая луну вдоль пути дуги.
Когда вы запускаете эту анимацию, она выглядит не очень хорошо. После того, как луна достигает вершины дуги, она начинает прыгать вокруг.
Чтобы понять ошибку, подумайте, что происходит, когда пользователь касается чуть ниже вершины дуги. Поскольку тег OnSwipe
имеет motion:touchAnchorSide="bottom"
MotionLayout
попытается провести расстояние между пальцем и нижней частью представления постоянной на протяжении всей анимации.
Но, поскольку нижняя часть луны не всегда идет в одном направлении, он поднимается, затем возвращается, MotionLayout
не знает, что делать, когда пользователь только что прошел верхнюю часть дуги. Чтобы рассмотреть это, поскольку вы отслеживаете дно Луны, где его следует размещать, когда пользователь здесь касается?
Шаг 2: Используйте правую сторону
Чтобы избежать таких ошибок, важно всегда выбирать touchAnchorId
и touchAnchorSide
, которые всегда прогрессируют в одном направлении на протяжении всего времени всей анимации.
В этой анимации как right
, так и left
сторона Луны будут развиваться через экран в одном направлении.
Тем не менее, как bottom
, так и top
будут обращать внимание на направление. Когда OnSwipe
пытается отследить их, это будет запутаться, когда их направление изменится.
- Чтобы эта анимация следила за событиями прикосновения, измените
touchAnchorSide
right
.
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="right"
/>
Шаг 3: Используйте Dragdection
Вы также можете объединить dragDirection
с touchAnchorSide
чтобы сделать боковую дорожку другим направлением, чем обычно. Все еще важно, чтобы touchAnchorSide
прогрессировал только в одном направлении, но вы можете сказать MotionLayout
, в каком направлении отслеживать. Например, вы можете сохранить touchAnchorSide="bottom"
, но добавить dragDirection="dragRight"
. Это приведет к тому, что MotionLayout
отслеживает положение нижнего вида, но рассматривает его местонахождение только при движении вправо (он игнорирует вертикальное движение). Таким образом, хотя дно идет вверх и вниз, оно все равно будет правильно оживить с помощью OnSwipe
.
-
OnSwipe
, чтобы правильно отслеживать движение Луны.
step7.xml
<!-- Using dragDirection to control the direction of drag tracking →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
motion:dragDirection="dragRight"
/>
Попробуйте это
- Запустите приложение снова и попробуйте перетащить луну по всему пути. Несмотря на то, что это следует за сложной дугой,
MotionLayout
сможет прогрессировать анимацию в ответ на события смахивания.
11. Запуск движения с кодом
MotionLayout
может использоваться для создания богатых анимаций при использовании с CoordinatorLayout
. На этом этапе вы построите складной заголовок, используя MotionLayout
.
Шаг 1: Исследуйте существующий код
- Чтобы начать, открыть
layout/activity_step8.xml
. - В
layout/activity_step8.xml
вы видите, что работающийCoordinatorLayout
иAppBarLayout
уже создан.
Activity_step8.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
...>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="180dp">
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
... >
...
</androidx.constraintlayout.motion.widget.MotionLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
...
motion:layout_behavior="@string/appbar_scrolling_view_behavior" >
...
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
В этом макете используется CoordinatorLayout
для обмена информацией о прокрутке между NestedScrollView
и AppBarLayout
. Таким образом, когда вложенный прокручивается NestedScrollView
, он расскажет AppBarLayout
об изменении. Вот как вы реализуете панель инструментов, подобную этой, на Android - прокрутка текста будет «скоординирована» с обезжиривающим заголовком.
Сцена движения, на которую указывает @id/motion_layout
, похожа на сцену движения на последнем шаге. Тем не менее, декларация OnSwipe
была удалена, чтобы позволить ему работать с CoordinatorLayout
.
- Запустите приложение и перейдите к шагу 8 . Вы видите это, когда вы прокручиваете текст, луна не движется.
Шаг 2: Сделайте прокрутку MotionLayout
- Чтобы сделать прокрутку представления
MotionLayout
, как толькоNestedScrollView
прокрутки CrollView добавьтеmotion:minHeight
иmotion:layout_scrollFlags
вMotionLayout
.
Activity_step8.xml
<!-- Add minHeight and layout_scrollFlags to the MotionLayout -->
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
motion:layoutDescription="@xml/step8"
motion:motionDebug="SHOW_PATH"
android:minHeight="80dp"
motion:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed" >
- Запустите приложение снова и перейдите к шагу 8 . Вы видите, что
MotionLayout
рушится, когда вы прокручиваете вверх. Тем не менее, анимация пока не прогрессирует на основе поведения прокрутки.
Шаг 3: Переместите движение с кодом
- Open
Step8Activity.kt
. Отредактируйте функциюcoordinateMotion()
, чтобы рассказатьMotionLayout
об изменениях в положении прокрутки.
Step8activity.kt
// TODO: set progress of MotionLayout based on an AppBarLayout.OnOffsetChangedListener
private fun coordinateMotion() {
val appBarLayout: AppBarLayout = findViewById(R.id.appbar_layout)
val motionLayout: MotionLayout = findViewById(R.id.motion_layout)
val listener = AppBarLayout.OnOffsetChangedListener { unused, verticalOffset ->
val seekPosition = -verticalOffset / appBarLayout.totalScrollRange.toFloat()
motionLayout.progress = seekPosition
}
appBarLayout.addOnOffsetChangedListener(listener)
}
Этот код зарегистрирует OnOffsetChangedListener
, который будет вызывать каждый раз, когда пользователь прокручивает с текущим смещением прокрутки.
MotionLayout
поддерживает их переход, установив свойство Progress. Чтобы преобразовать между verticalOffset
и процентным прогрессом, разделите на общий диапазон прокрутки.
Попробуйте это
- Разверните приложение снова и запустите анимацию шага 8 . Вы видите, что
MotionLayout
будет развивать анимацию на основе позиции прокрутки.
Можно создать пользовательские динамические анимации панели инструментов с использованием MotionLayout
. Используя последовательность KeyFrames
вы можете достичь очень смелых эффектов.
12. Поздравляю
Этот CodeLab охватывал базовый API MotionLayout
.
Чтобы увидеть больше примеров MotionLayout
на практике, ознакомьтесь с официальной выборкой . И обязательно ознакомьтесь с документацией !
Узнать больше
MotionLayout
поддерживает еще больше функций, не рассматриваемых в этом CodeLab, таких как KeyCycle,
который позволяет вам контролировать пути или атрибуты с повторяющимися циклами, и KeyTimeCycle,
который позволяет вам оживить в зависимости от времени. Проверьте образцы для примеров каждого.
Ссылки на другие коделабы в этом курсе см. Веденный Android на целевой странице Kotlin CodeLabs .