MotionLayout
是一种布局类型,可帮助您管理应用中的动作和微件动画。MotionLayout
是 ConstraintLayout
的子类,在其丰富的布局功能基础之上构建而成。作为 ConstraintLayout
库的一部分,MotionLayout
可用作支持库。
MotionLayout
缩小了布局转换与复杂运动处理之间的差距,同时在属性动画框架、TransitionManager
和 CoordinatorLayout
之间提供了各种功能。
除了描述布局之间的转换之外,MotionLayout
还允许您为任何布局属性添加动画效果。此外,它本身就支持可搜索转换。也就是说,您可以根据某个条件(例如触控输入)立即显示转换中的任意点。MotionLayout
还支持关键帧,从而实现完全自定义的转换以满足您的需求。
MotionLayout
是完全声明性的,也就是说您可以使用 XML 描述任何转换,无论复杂程度如何。
设计考虑事项
MotionLayout
旨在移动与用户交互的界面元素(例如按钮和标题栏),以及对其调整大小和添加动画效果。请勿在应用中将动作用作无意义的特殊效果。您可以用它来帮助用户了解您的应用在做什么。如需详细了解如何设计包含运动的应用,请参阅 Material Design 的了解运动部分。
开始使用
请按照以下步骤开始在您的项目中使用 MotionLayout
。
-
添加
ConstraintLayout
依赖项:如需在项目中使用MotionLayout
,请将ConstraintLayout
2.0 依赖项添加到应用的build.gradle
文件中。如果您使用了 AndroidX,请添加以下依赖项:dependencies {
implementation "androidx.constraintlayout:constraintlayout:2.2.0-beta01"
// To use constraintlayout in compose
implementation "androidx.constraintlayout:constraintlayout-compose:1.1.0-beta01"
}dependencies {
implementation("androidx.constraintlayout:constraintlayout:2.2.0-beta01")
// To use constraintlayout in compose
implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-beta01")
} -
创建
MotionLayout
文件:MotionLayout
是ConstraintLayout
的子类,因此您可以通过替换布局资源文件中的类名称,将任何现有的ConstraintLayout
转换为MotionLayout
,如下面的示例所示:<!-- 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 所示的布局:<?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>
-
创建 MotionScene:在前面的
MotionLayout
示例中,app:layoutDescription
属性引用了一个运动场景。运动场景是一个 XML 资源文件。在其<MotionScene>
根元素中,动作场景包含相应布局的所有动作描述。为了使布局信息与运动描述分开,每个MotionLayout
都会引用单独的运动场景。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 所示。
在每个 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"
表示是否应用动作场景。此属性的默认值为true
。app:showPaths="boolean"
指示是否在运动运行时显示运动路径。此属性的默认值为false
。app:progress="float"
可让您明确指定转换进度。您可以使用从0
(转换开始)到1
(转换结束)之间的任意浮点值。app:currentState="reference"
可让您指定具体的ConstraintSet
。app:motionDebug
可让您显示与运动有关的其他调试信息。可能的值为"SHOW_PROGRESS"
、"SHOW_PATH"
或"SHOW_ALL"
。
其他资源
如需详细了解 MotionLayout
,请参阅以下资源:
- 使用 Kotlin 03.2 进行高级 Android 开发:使用 MotionLayout 实现动画效果
- MotionLayout 示例
- GitHub 上的 MotionLayout/ConstraintLayout 示例
- MotionLayout 简介(第 I 部分)
- MotionLayout 简介(第 II 部分)
- MotionLayout 简介(第 III 部分)
- MotionLayout 简介(第 IV 部分)