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

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

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

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

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

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

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

Рекомендации по дизайну

MotionLayout предназначена для перемещения, изменения размера и анимации элементов интерфейса, с которыми взаимодействуют пользователи, например кнопок и строк заголовка. Не используйте движение в приложении в качестве бесплатного спецэффекта. Это поможет пользователям понять, что делает ваше приложение. Дополнительную информацию о разработке приложений с анимацией можно найти в разделе Understanding motion (Общие сведения об анимации) руководства по Material Design.

Начать

Чтобы начать использовать MotionLayout в своем проекте, выполните следующие действия:

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

    классный

    dependencies {
        implementation "androidx.constraintlayout:constraintlayout:2.2.2"
        // To use constraintlayout in compose
        implementation "androidx.constraintlayout:constraintlayout-compose:1.1.2"
    }

    Котлин

    dependencies {
        implementation("androidx.constraintlayout:constraintlayout:2.2.2")
        // To use constraintlayout in compose
        implementation("androidx.constraintlayout:constraintlayout-compose:1.1.2")
    }
  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 ссылается на motion scene. Сцена движения – это XML-файл ресурсов. В корневом элементе <MotionScene> содержатся все описания анимации для соответствующего макета. Чтобы информация о макете не смешивалась с описанием движения, каждый элемент MotionLayout ссылается на отдельную сцену движения. Определения в сцене движения имеют приоритет над аналогичными определениями в 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. Геттер и сеттер должны соответствовать определенному шаблону. Например, поддерживается 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 можно найти в следующих ресурсах: