MotionLayout
本文是对Introduction to MotionLayout的翻译,自己可能翻译的有点不好,欢迎大家批评指正。侵权删。
MotionLayout是一个在ConstraintLayout2.0版本库的时候添加的一个新的类,可以帮助开发者处理一些动画。
Android framework已经提供了如此多的方式来帮我们在应用中添加动画,
- Drawable矢量图
- 属性动画
- CoordinatorLayout
- LayoutTransition animations
- 利用TransitionManager的Layout transitions
那么MotionLayout与它们有什么不同的地方?
- MotionLayout是ConstraintLayout的一个子类,受益于ConstraintLayout丰富的特性
- MotionLayout让layout布局元素能够在动画过渡期间也能够响应用户的UI交互
什么时候使用它?
当想要对与用户有直接交互的UI元素做动画时。
什么是对与用户有着直接交互的UI元素?就是用户的点击,滑动等动作。
添加MotionLayout到你的项目
dependencies {
implementation 'com.android.support.constraint:constraint-layout:2.0.0-alpha2'
}
使用MotionLayout
MotionLayout是ConstraintLayout的子类。要想已有的ConstraintLayout变成MotionLayout只需要在具体的xml中,将:
<android.support.constraint.ConstraintLayout .../>
改成
<android.support.constraint.motion.MotionLayout .../>
MotionLayout动画的具体的描述文件并没有包含在定义MotionLayout的layout文件中,而是定义在了一个单独的xml文件(一个MotionScene
文件)。
MotionScene
就如同之前所说,描述MotionLayout的动画的具体信息被定义在了一个单独的xml文件,MotionScene,通常存储在res/xml
目录下
如图所述,一个MotionScene通常包含如下的内容
- 所使用的ConstraintSets
- 所使用的ConstraintSets的transition(过渡阶段)
- keyframes(关键帧)
就像下面的例子,我们通过使用手指拖动一个简单的view从屏幕的一端滑动至另一端,如何做到呢?
实现1:引用已有的layouts
一个指向初始的layout(view在屏幕左端),一个指向结束的layout(view在屏幕的右端),我们需要定义两个layout
view在左端的layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/button"
android:background="@color/colorAccent"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="8dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
view在右端的layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/button"
android:background="@color/colorAccent"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginEnd="8dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
然后定义一个MotionLayout文件
<?xml version="1.0" encoding="utf-8"?>
<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>
注意这个layout文件指向了一个MotionScene文件---scene_01
<?xml version="1.0" encoding="utf-8"?>
<MotionScene
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetStart="@layout/motion_01_cl_start"
motion:constraintSetEnd="@layout/motion_01_cl_end"
motion:duration="1000">
<OnSwipe
motion:touchAnchorId="@+id/button"
motion:touchAnchorSide="right"
motion:dragDirection="dragRight" />
</Transition>
</MotionScene>
scene_01
通过指定开始和结束的ConstraintSet(motion_01_cl_start
和motion_01_cl_end
,规定了过渡动画。注意,我们指定了OnSwipe
标签在transition中
OnSwipe标签
在scene_01.xml
文件中,我们指定了一个OnSwipe
标签,在一个Transition
标签中,作用是依据我们的手指的动作,来让动画发生。
OnSwipe
的具体的各种属性
touchAnchorId
:我们想要做动画的object对象,(这里是@+id/button
)
touchAnchorSide
:object的边,(right/left/top/bottom)
dragDirection
:我们所想要追踪的手指动作的方向(dragRight/dragLeft/dragUp/dragDown)
实现2:将ConstraintSets直接定义在MotionScene中
第一种实现方式重用了layout(基于ConstraintLayout)
MotionLayout同样支持直接在MotionScene中定义ConstraintSets,这带来了如下优势:
- 只需要一个文件,就可以包括所有的ConstraintSets
- 方便处理attributes
- 未来Android Studio将只支持这种将ConstraintSets直接定义在MotionScene中的方式
让我们将ConstraintSets直接定义在MotionScene中,达到和实现1一样的效果
我们定义一个与实现1中一样的MotionLayout文件,只需要引用scene_02.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.motion.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/motionLayout"
app:layoutDescription="@xml/scene_02"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/button"
android:background="@color/colorAccent"
android:layout_width="64dp"
android:layout_height="64dp"
android:text="Button" />
</android.support.constraint.motion.MotionLayout>
在scene_02.xml
中,Transition
标签内部是一致的,区别在于我们直接将start
和end
的Constrait定义在了文件中。并由一个ConstraintSet
标签包含着。
<?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>
可以被自动插值的属性(Interpolated Attributes)
在Constraint标签中除了可以定义元素的位置之外,还可以定义一些其他的属性,MotionLayout将自动地为这些属性生成中间的过渡状态,也就是插值。
alpha
visibility
elevation
rotation,rotation[x / y]
translation[x / y / z]
scaleX / Y
类似于下面的形式
<Constraint
android:id="@id/content"
android:layout_width="0dp"
android:layout_height="match_parent"
motion:layout_constraintWidth_default="percent"
motion:layout_constraintWidth_percent="1"
android:scaleX="0.8"
android:scaleY="0.8"
motion:layout_constraintLeft_toRightOf="@+id/button"
motion:layout_constraintTop_toTopOf="parent"/>
MotionLayout可以定义的属性
MotionLayout提供了一些属性
app:layoutDescription="reference"
,指向一个MotionScene XML文件
app:applyMotionScene="boolean"
是否使用MotionScene,默认为true
app:showPaths="boolean"
展示motion 路径,默认为false,app正式发布的时候要记得关闭
app:pregress="float"
明确指定中间进度,区间在0到1
app:currentState="reference"
强制一个特定的ConstraintSet
这篇文章只是MotionLayout的基础的功能的一个介绍,MotionLayout能够实现的功能远不止如此。