Russian (Pусский) translation by Ellen Nelson (you can also view the original English article)
Благодаря своей замечательной универсальности виджет ConstraintLayout
стал «швейцарским ножом» для Android разработчиков при создании макетов. Добавление сложной анимации с ним возможно, однако это может быть довольно затратным по времени. Вот почему, на I/O 2018 Google выкатила виджет MotionLayout
.
Виджет MotionLayout
, который теперь является частью библиотеки поддержки Android, дополняет виджет ConstraintLayout
. Это уникальный виджет, с которым вы можете анимировать его содержимое, используя только XML. Кроме того, он даёт вам детальный контроль над всей его анимацией.
В этом уроке я покажу вам, как добавить его в проект Android Studio и создать несколько разных анимаций с его помощью.
Что необходимо
Для работы с этим руководством вам потребуется:
- Android Studio 3.1.3 или выше
- устройство или эмулятор с Android API уровня 21 или выше
- общее представление о виджете
ConstraintLayout
1. Добавление зависимостей
Чтобы использовать виджет MotionLayout
в проекте Android Studio, в качестве зависимости implementation
, у вас должна быть последняя версия библиотеки Constraint Layout. А чтобы избежать конфликта версий, убедитесь, что включили зависимость для последней стабильной 7 версии библиотеки appcompat.
Соответственно, добавьте следующий код в файл build.gradle модуля app
:
implementation 'com.android.support:appcompat-v7:27.0.2' implementation 'com.android.support.constraint:constraint-layout:2.0.0-alpha1'
2. Задаём макет
Виджет MotionLayout
может делать всё, на что способен виджет ConstraintLayout
. Таким образом вы можете спокойно заменить один на другой. Однако сейчас, я советую создать новый XMLфайл макета и добавить в него виджет MotionLayout
в качестве корневого элемента.
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.motion.MotionLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/motion_container"> <!-- More code here --> </android.support.constraint.motion.MotionLayout>
В этом уроке мы будем анимировать виджет ImageView
. Так, что добавьте его в качестве первого дочернего элемента.
<ImageView android:id="@+id/actor" app:srcCompat="@color/colorAccent" android:layout_width="wrap_content" android:layout_height="wrap_content" />
Для виджета ImageView
можете использовать любую графику drawable. В коде выше, я использую drawable с цветом.
Дальше добавляем кнопку, нажав на которую, запустится анимация. Следующий код показывает, как расположить её по центру макета:
<Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Press Me" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" android:onClick="start"/>
Кроме того, для отслеживания хода выполнения анимации, добавьте в макет виджет SeekBar
и поместите его под кнопкой. Вот как:
<SeekBar android:layout_width="100dp" android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@+id/button" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="10dp" android:id="@+id/seekbar"/>
И, наконец, поскольку с кнопкой связан обработчик события «on-click», убедитесь, что вы указали это в вашей activity.
fun start(v: View) { // More code here }
3. Создание сцены движения
Возможно, вы заметили, что в виджет ImageView
мы не добавили ни один контейнер. Это потому, что мы добавим их в цену с движением. Сцена движения – это XML файл, содержащий данные об анимации, которую вы желаете получить с помощью виджета MotionLayout
.
Для создания сцены, создаём XML файл ресурсов и добавляем в него элемент MotionScene
.
<?xml version="1.0" encoding="utf-8"?> <MotionScene xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <!-- More code here --> </MotionScene>
Сцена движения содержит элементы ConstraintSet
, определяющие регуляторы, которые должны применяться к виджету в разных моментах анимации. Файлы сцены движения обычно содержат два набора регулятора: один для начала анимации, а другой для окончания.
В следующем коде показано, как создать два набора регуляторов, которые помогут виджету MotionLayout
переместить виджет ImageView
из нижнего правого угла экрана в верхний левый:
<ConstraintSet android:id="@+id/starting_set"> <Constraint android:id="@+id/actor" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent" android:layout_width="60dp" android:layout_height="60dp" /> </ConstraintSet> <ConstraintSet android:id="@+id/ending_set"> <Constraint android:id="@+id/actor" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent" android:layout_width="60dp" android:layout_height="60dp" /> </ConstraintSet>
Обратите внимание, что каждый элемент ConstraintSet
всегда должен указывать как желаемую позицию, так и желаемый размер. Это важно, потому что это изменит любую установленную ранее информацию макета.
Чтобы виджет MotionLayout
понял порядок, в котором должны применяться наборы регуляторов, вы должны создать элемент Transition
. С помощью интуитивно названных атрибутов constraintSetStart
и constraintSetEnd
, вы можете указать, какой набор должен применяться первым, а какой последним. Элемент Transition
также позволяет указать продолжительность анимации.
<Transition android:id="@+id/my_transition" app:constraintSetStart="@+id/starting_set" app:constraintSetEnd="@+id/ending_set" app:duration="2000"> </Transition>
На этом этапе, сцена движения готова. Однако виджет MotionLayout
все ещё не знает об этом. Итак, вернемся к XML-файлу макета, добавим в виджет атрибут layoutDescription
и установим его значение равным названию файла сцены движения.
Если название вашего файла сцены движения my_scene.xml, ваш виджет MotionLayout
теперь должен выглядеть следующим образом:
<android.support.constraint.motion.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" app:layoutDescription="@xml/my_scene" android:id="@+id/motion_container"> ... </android.support.constraint.motion.MotionLayout>
4. Запуск анимации
Когда вы запускаете приложение, виджет MotionLayout
автоматически применяет набор регуляторов, указанный в атрибуте constraintSetStart
элемента Transition
. Таким образом, для запуска анимации, всё, что нужно сделать, это вызвать метод transitionToEnd()
виджета. В следующем коде, который необходимо добавить в обработчик событий по-клику, созданный на предыдущем шаге, показано, как это сделать:
motion_container.transitionToEnd()
На этом этапе, если вы запустите приложение и нажмёте кнопку, вы сможете увидеть, как виджет ImageView
плавно перемещается по экрану.
5. Обработка событий анимации
Прикрепив объект TransitionListener
к виджету MotionLayout
, вы можете тщательно отслеживать ход анимации.
motion_container.setTransitionListener( object: MotionLayout.TransitionListener { // More code here } )
Интерфейс TransitionListener
имеет два абстрактных метода, и Android Studio автоматически сгенерирует для них заглушки.
Метод onTransitionCompleted()
вызывается, когда завершается переход от одного набора регуляторов к другому. А пока давайте используем его для сброса ограничений виджета ImageView
, вызвав внутри него метод transitionToStart()
.
override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) { if(currentId == R.id.ending_set) { // Return to original constraint set motion_container.transitionToStart() } }
Метод onTransitionChange()
вызывается каждый раз, когда изменяется ход анимации. Так что, ход выполнения является числом с плавающей запятой, которое лежит между нулём и единицей. Следующий код показывает, как обновить SeekBar
в зависимости по ходу анимации:
override fun onTransitionChange(motionLayout: MotionLayout?, startId: Int, endId: Int, progress: Float) { seekbar.progress = ceil(progress * 100).toInt() }
А теперь давайте снова запустим приложение, чтобы увидеть две анимации.
6. Создание ключевых кадров
В нашей анимации, виджет ImageView
перемещается по траектории, которая выглядит как прямая линия. Это связано с тем, что виджету MotionLayout
задано только две точки: начальная точка, которая находится в нижнем правом углу экрана, и конечная точка, которая находится в верхнем левом углу экрана. Если вы хотите изменить форму маршрута, вам нужно будет указать несколько промежуточных точек между начальной и конечной точками. Для этого вам нужно создать новые ключевые кадры.
Прежде чем начать создавать ключевые кадры, вы должны добавить элемент KeyFrameSet
к элементу Transition
вашей сцены движения. Внутри этого нового элемента можно создавать любое количество ключевых кадров.
<KeyFrameSet android:id="@+id/my_keys"> <!-- More code here --> </KeyFrameSet>
Виджет MotionLayout
поддерживает множество различных типов ключевых кадров. В этом уроке мы будем работать только с двумя видами: KeyPosition
и KeyCycle
.
Кадры KeyPosition
помогают вам изменить форму траектории. При их создании убедитесь, что вы указали идентификатор целевого виджета, позицию на временной шкале, которая может быть любым числом от 0 до 100, и желаемые координаты X или Y, заданные в процентах. Координаты могут быть либо относительно фактических осей X или Y, либо относительно самой траектории.
В следующем коде показано, как создать два ключевых кадра, благодаря которым, виджет ImageView
следует по нашему пути, избегая столкновений с кнопкой и строкой поиска:
<KeyPosition app:target="@+id/actor" app:framePosition="30" app:type="deltaRelative" app:percentX="0.85" /> <KeyPosition app:target="@+id/actor" app:framePosition="60" app:type="deltaRelative" app:percentX="1" />
Если вы запустите приложение сейчас, вы увидите анимацию, которая выглядит следующим образом:
Вы, конечно, можете добавить больше ключевых кадров. Например, добавив следующий ключевой кадр к концу временной шкалы, вы можете заставить виджет ImageView
следовать по более волнистой траектории:
<KeyPosition app:target="@+id/actor" app:framePosition="80" app:type="deltaRelative" app:percentX="0.5" />
При использовании кадра KeyCycle
вместе с кадрами KeyPosition
, вы можете придать анимации колебания. Для этого, вы должны снова указать идентификатор целевого виджета, положение по временной шкале и желаемое значение, на которое анимация будет отклоняться туда-сюда. Кроме того, вы должны сконфигурировать раскачку, указав такие данные, как форма и период волны.
Следующий код создаёт кадр KeyCycle
, который использует синусоидальную траекторию для поворота виджета ImageView
на 50 градусов, время от времени:
<KeyCycle app:target="@+id/actor" app:framePosition="30" android:rotation="50" app:waveShape="sin" app:wavePeriod="1" />
При повторном запуске приложения, вы должны увидеть анимацию, которая выглядит следующим образом:
7. Сделаем анимированный виджет интерактивным
Все это время, для запуска анимации, вы нажимали кнопку. Однако, такая кнопка не всегда нужна, поскольку виджет MotionLayout
позволяет прикреплять обработчики сенсорных событий напрямую к анимируемым виджетам. В настоящее время он поддерживает события нажатия и «листания».
Например, вы можете добавить следующий элемент OnClick
, который предназначен для виджета ImageView
, внутри элемента Transition
вашей сцены движения и избавиться от кнопки:
<OnClick app:target="@+id/actor" app:mode="transitionToEnd"/>
Точно так же, вы можете использовать элемент OnSwipe
, чтобы позволить пользователю вручную перетаскивать виджет ImageView
по экрану. При создании элемента вы должны убедиться, что указали правильное направление перетаскивания и сторону виджета, которая должна выступать в качестве ручки для перемещения.
<OnSwipe app:touchAnchorId="@+id/actor" app:touchAnchorSide="top" app:dragDirection="dragUp" />
Если вы снова запустите приложение, вы сможете перетащить виджет ImageView
.
Заключение
Теперь вы знаете, как использовать виджет MotionLayout
для быстрого добавления сложных интерактивных анимаций в ваши Android приложения. Вы можете быть уверены, анимация будет работать без задержек или дрожания на большинстве устройств, если вы обошлись без вложенных видов.
Стоит отметить, что в будущих версиях Android Studio будет визуальный редактор движения, который, вероятно, ещё больше улучшит удобство использования виджета.
Узнать больше можете из этой официальной документации.
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post