Управляйте анимацией движения и виджетов с помощью MotionLayout

Попробуйте способ создания композиций.
Jetpack Compose — рекомендуемый набор инструментов для создания пользовательского интерфейса для Android. Узнайте, как использовать анимацию в Compose.

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

MotionLayout устраняет разрыв между переходами между элементами макета и сложной обработкой движения, предлагая сочетание функций, заимствованных из фреймворка анимации свойств , TransitionManager и CoordinatorLayout .

Рисунок 1. Базовое сенсорное управление движением.

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

MotionLayout — это полностью декларативный инструмент, то есть вы можете описать любые переходы в XML, независимо от их сложности.

Вопросы проектирования

MotionLayout предназначен для перемещения, изменения размера и анимации элементов пользовательского интерфейса, с которыми взаимодействуют пользователи, таких как кнопки и заголовки. Не используйте анимацию в своем приложении как ненужный спецэффект. Используйте ее, чтобы помочь пользователям понять, что делает ваше приложение. Для получения дополнительной информации о разработке приложений с использованием анимации см. раздел Material Design «Понимание анимации» .

Начать

Выполните следующие шаги, чтобы начать использовать MotionLayout в своем проекте.

  1. Добавьте зависимость ConstraintLayout : чтобы использовать MotionLayout в вашем проекте, добавьте зависимость ConstraintLayout 2.0 в файл build.gradle вашего приложения. Если вы используете AndroidX, добавьте следующую зависимость:

    классный

    dependencies {
        implementation "androidx.constraintlayout:constraintlayout:2.2.1"
        // To use constraintlayout in compose
        implementation "androidx.constraintlayout:constraintlayout-compose:1.1.1"
    }

    Котлин

    dependencies {
        implementation("androidx.constraintlayout:constraintlayout:2.2.1")
        // To use constraintlayout in compose
        implementation("androidx.constraintlayout:constraintlayout-compose:1.1.1")
    }
  2. Создайте файл MotionLayout : MotionLayout является подклассом ConstraintLayout , поэтому вы можете преобразовать любой существующий ConstraintLayout в MotionLayout , заменив имя класса в файле ресурсов макета, как показано в следующих примерах:

    AndroidX

    <!-- before: ConstraintLayout -->
    <androidx.constraintlayout.widget.ConstraintLayout .../>
    <!-- after: MotionLayout -->
    <androidx.constraintlayout.motion.widget.MotionLayout .../>
              

    Библиотека поддержки

    <!-- before: ConstraintLayout -->
    <android.support.constraint.ConstraintLayout .../>
    <!-- after: MotionLayout -->
    <android.support.constraint.motion.MotionLayout .../>
              

    Вот полный пример файла MotionLayout , определяющего макет, показанный на рисунке 1:

    AndroidX

    <?xml version="1.0" encoding="utf-8"?>
    <!-- activity_main.xml -->
    <androidx.constraintlayout.motion.widget.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:id="@+id/motionLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutDescription="@xml/scene_01"
        tools:showPaths="true">
    
        <View
            android:id="@+id/button"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:background="@color/colorAccent"
            android:text="Button" />
    
    </androidx.constraintlayout.motion.widget.MotionLayout>
            

    Библиотека поддержки

    <?xml version="1.0" encoding="utf-8"?>
    <!-- activity_main.xml -->
    <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:id="@+id/motionLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutDescription="@xml/scene_01"
        tools:showPaths="true">
    
        <View
            android:id="@+id/button"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:background="@color/colorAccent"
            android:text="Button" />
    
    </android.support.constraint.motion.MotionLayout>
            
  3. Создание MotionScene: в предыдущем примере MotionLayout атрибут app:layoutDescription ссылается на MotionScene . MotionScene — это XML-файл ресурсов. Внутри своего корневого элемента <MotionScene> MotionScene содержит все описания движений для соответствующего макета. Чтобы информация о макете была отделена от описаний движений, каждый MotionLayout ссылается на отдельную MotionScene. Определения в MotionScene имеют приоритет над любыми аналогичными определениями в MotionLayout .

    Вот пример файла с описанием движения, демонстрирующего базовое горизонтальное движение на рисунке 1:

    <?xml version="1.0" encoding="utf-8"?>
    <MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:motion="http://schemas.android.com/apk/res-auto">
    
        <Transition
            motion:constraintSetStart="@+id/start"
            motion:constraintSetEnd="@+id/end"
            motion:duration="1000">
            <OnSwipe
                motion:touchAnchorId="@+id/button"
                motion:touchAnchorSide="right"
                motion:dragDirection="dragRight" />
        </Transition>
    
        <ConstraintSet android:id="@+id/start">
            <Constraint
                android:id="@+id/button"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:layout_marginStart="8dp"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintStart_toStartOf="parent"
                motion:layout_constraintTop_toTopOf="parent" />
        </ConstraintSet>
    
        <ConstraintSet android:id="@+id/end">
            <Constraint
                android:id="@+id/button"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:layout_marginEnd="8dp"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintEnd_toEndOf="parent"
                motion:layout_constraintTop_toTopOf="parent" />
        </ConstraintSet>
    
    </MotionScene>
        

    Обратите внимание на следующее:

    • <Transition> содержит базовое определение движения.

      • motion:constraintSetStart и motion:constraintSetEnd — это ссылки на конечные точки движения. Эти конечные точки определяются в элементах <ConstraintSet> далее в сцене движения.

      • motion:duration указывает количество миллисекунд, необходимое для завершения движения.

    • Функция <OnSwipe> позволяет настроить сенсорное управление движением.

      • motion:touchAnchorId обозначает область, которую пользователь может пролистывать и перетаскивать.

      • motion:touchAnchorSide означает, что элемент управления перетаскивается с правой стороны.

      • motion:dragDirection задает направление перетаскивания . Например, motion:dragDirection="dragRight" означает, что продвижение увеличивается по мере перетаскивания элемента вправо.

    • В разделе <ConstraintSet> вы определяете различные ограничения, описывающие ваше движение. В этом примере для каждой конечной точки вашего движения определен отдельный раздел <ConstraintSet> . Эти конечные точки центрируются по вертикали с помощью app:layout_constraintTop_toTopOf="parent" и app:layout_constraintBottom_toBottomOf="parent" . По горизонтали конечные точки находятся в крайнем левом и правом углах экрана.

    Для более подробного ознакомления с различными элементами, которые поддерживает сцена движения, см. примеры MotionLayout .

Интерполированные атрибуты

В файле сцены движения элементы ConstraintSet могут содержать дополнительные атрибуты, которые интерполируются во время перехода. Помимо позиции и границ, MotionLayout интерполирует следующие атрибуты:

  • alpha
  • visibility
  • elevation
  • rotation , rotationX , rotationY
  • translationX , translationY , translationZ
  • scaleX , scaleY

Пользовательские атрибуты

Внутри элемента <Constraint> можно использовать элемент <CustomAttribute> для указания перехода для атрибутов, которые не связаны напрямую с атрибутами позиции или View .

<Constraint
    android:id="@+id/button" ...>
    <CustomAttribute
        motion:attributeName="backgroundColor"
        motion:customColorValue="#D81B60"/>
</Constraint>

Объект <CustomAttribute> содержит два собственных атрибута:

  • motion:attributeName является обязательным и должен соответствовать объекту с методами getter и setter. Методы getter и setter должны соответствовать определенному шаблону. Например, поддерживается backgroundColor , поскольку представление имеет базовые методы getBackgroundColor() и setBackgroundColor() .
  • Другой атрибут, который необходимо указать, зависит от типа значения. Выберите один из следующих поддерживаемых типов:
    • motion:customColorValue для цветов
    • motion:customIntegerValue для целых чисел
    • motion:customFloatValue для чисел с плавающей запятой
    • motion:customStringValue для строк
    • motion:customDimension для размеров
    • motion:customBoolean для логических значений

При указании пользовательского атрибута задайте значения конечной точки как в начальном, так и в конечном элементах <ConstraintSet> .

Изменить цвет фона

Основываясь на предыдущем примере, предположим, что вы хотите, чтобы цвета элемента управления менялись в процессе его движения, как показано на рисунке 2.

Рисунок 2. Изображение меняет цвет фона по мере движения.

Добавьте элемент <CustomAttribute> к каждому элементу ConstraintSet , как показано в следующем фрагменте кода:

<ConstraintSet android:id="@+id/start">
    <Constraint
        android:id="@+id/button"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginStart="8dp"
        motion:layout_constraintBottom_toBottomOf="parent"
        motion:layout_constraintStart_toStartOf="parent"
        motion:layout_constraintTop_toTopOf="parent">
        <CustomAttribute
            motion:attributeName="backgroundColor"
            motion:customColorValue="#D81B60" />
    </Constraint>
</ConstraintSet>

<ConstraintSet android:id="@+id/end">
    <Constraint
        android:id="@+id/button"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginEnd="8dp"
        motion:layout_constraintBottom_toBottomOf="parent"
        motion:layout_constraintEnd_toEndOf="parent"
        motion:layout_constraintTop_toTopOf="parent">
        <CustomAttribute
            motion:attributeName="backgroundColor"
            motion:customColorValue="#9999FF" />
    </Constraint>
</ConstraintSet>

Дополнительные атрибуты MotionLayout

Помимо атрибутов, приведенных в предыдущем примере, MotionLayout имеет и другие атрибуты, которые вы можете указать:

  • app:applyMotionScene="boolean" указывает, следует ли применять анимационную сцену. Значение по умолчанию для этого атрибута — true .
  • app:showPaths="boolean" указывает, следует ли отображать траектории движения во время его выполнения. Значение по умолчанию для этого атрибута — false .
  • app:progress="float" позволяет явно указать ход перехода. Вы можете использовать любое значение с плавающей запятой от 0 (начало перехода) до 1 (конец перехода).
  • app:currentState="reference" позволяет указать конкретный ConstraintSet .
  • app:motionDebug позволяет отображать дополнительную отладочную информацию о движении. Возможные значения: "SHOW_PROGRESS" , "SHOW_PATH" или "SHOW_ALL" .

Дополнительные ресурсы

Для получения дополнительной информации о MotionLayout см. следующие ресурсы: