使用 MotionLayout 管理运动和微件动画

MotionLayout 是一种布局类型,可帮助您管理应用中的运动和微件动画。MotionLayoutConstraintLayout 的子类,在其丰富的布局功能基础之上构建而成。作为 ConstraintLayout 库的一部分,MotionLayout 可用作支持库,并可向后兼容 API 级别 14

图 1.基本的触控运动。

MotionLayout 缩小了布局转换与复杂运动处理之间的差距,同时在属性动画框架TransitionManagerCoordinatorLayout 之间提供了各种功能。

除了描述布局之间的转换之外,MotionLayout 还能够让您为任何布局属性添加动画效果。此外,它本身就支持可搜索转换。也就是说,您可以根据某个条件(例如触控输入)立即显示转换中的任意点。MotionLayout 还支持关键帧,从而实现完全自定义的转换以满足您的需求。

MotionLayout 是完全声明性的,也就是说您可以使用 XML 描述任何转换,无论复杂程度如何。

设计考虑事项

MotionLayout 旨在移动与用户交互的界面元素(例如按钮和标题栏),以及对其调整大小和添加动画效果。您应用中的运动不应仅仅是无意义的特殊效果。它应该用于帮助用户了解您的应用在做什么。如需详细了解如何设计包含运动的应用,请参阅 Material Design 有关了解运动的部分。

使用入门

请按照以下步骤开始在您的项目中使用 MotionLayout

  1. 添加 ConstraintLayout 依赖项:要在项目中使用 MotionLayout,请向应用的 build.gradle 文件添加 ConstraintLayout 2.0 依赖项。如果您使用了 AndroidX,请添加以下依赖项:

        dependencies {
            implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1'
        }
            

    如果您没有使用 AndroidX,请添加以下支持库依赖项:

        dependencies {
            implementation 'com.android.support.constraint:constraint-layout:2.0.0-beta1'
        }
            
  2. 创建 MotionLayout 文件MotionLayoutConstraintLayout 的子类,因此您可以通过替换布局资源文件中的类名称,将任何现有的 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 资源文件,其中包含相应布局的所有运动描述。为了将布局信息与运动描述分开,每个 MotionLayout 都引用一个单独的 MotionScene。请注意,MotionScene 中的定义优先于 MotionLayout 中的任何类似定义。

    下面的示例 MotionScene 文件描述了图 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:constraintSetStartmotion:constraintSetEnd 指的是运动的端点。这些端点在 MotionScene 后面的 <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")。在水平方向上,端点位于屏幕最左侧和最右侧。

    如需详细了解 MotionScene 支持的各种元素,请参阅 MotionLayout 示例

插入的属性

在 MotionScene 文件中,ConstraintSet 元素可包含在转换期间插入的其他属性。除了位置和边界之外,MotionLayout 还插入以下属性:

  • alpha
  • visibility
  • elevation
  • rotationrotationXrotationY
  • translationXtranslationYtranslationZ
  • scaleXscaleY

自定义属性

<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.视图会在移动的过程中更改其背景色。

在每个 ConstraintSet 元素中添加 <CustomAttribute> 元素,如下所示:

    <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" 表示是否应用 MotionScene。此属性的默认值为 true
  • app:showPaths="boolean" 表示在运动进行时是否显示运动路径。此属性的默认值为 false
  • app:progress="float" 可让您明确指定转换进度。您可以使用从 0(转换开始)到 1(转换结束)之间的任意浮点值。
  • app:currentState="reference" 可让您指定具体的 ConstraintSet
  • app:motionDebug 可让您显示与运动有关的其他调试信息。可能的值为“SHOW_PROGRESS”、“SHOW_PATH”或“SHOW_ALL”。

其他资源

如需有关 MotionLayout 的详细信息,请参阅以下链接: