MotionLayout 介绍
MotionLayout 是在 ConstraintLayout 2.0 版本中引入的。ConstraintLayout 2.0 为 Android 开发者带来了许多新功能,其中最引人注目的就是 MotionLayout。MotionLayout 是 ConstraintLayout 的一个子类,专门用于创建复杂的动画效果和过渡。通过 MotionLayout,开发者可以在布局文件中定义多个状态,并在这些状态之间进行平滑的过渡,从而实现丰富的动画效果,例如转场动画、导航菜单的展开与收起等。
配置
ConstraintLayout库升级到2.0以上
implementation "androidx.constraintlayout:constraintlayout:2.1.2"
ConstraintLayout更新日志:developer.android.google.cn/jetpack/and…
XML根布局替换成MotionLayout
方式1:在XML布局找到Componet Tree,分别点击2、3位置就可以将根布局的ConstraintLayout转换为MotionLayout了;
方式2:当然,也可以手动修改!☺
若果通过方式1,AS会自动在 res/xml/下帮我们创建一个根元素为MotionScene的xml文件:
文件如下:
<?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:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start"
motion:duration="1000">
<!--设置关键帧时使用-->
<KeyFrameSet></KeyFrameSet>
</Transition>
<!--动画开始状态-->
<ConstraintSet android:id="@+id/start">
</ConstraintSet>
<!--动画结束状态-->
<ConstraintSet android:id="@+id/end">
</ConstraintSet>
</MotionScene>
那么MotionScene如何跟MotionLayout 产生关联的呢?通过app:layoutDescription
关联的:
<androidx.constraintlayout.motion.widget.MotionLayout
......
app:layoutDescription="@xml/motion_layout_anim" //通过这里关联动画文件
app:motionDebug="SHOW_ALL" //显示调试信息
app:showPaths="true"> //显示动画路径
......
</androidx.constraintlayout.motion.widget.MotionLayout >
MotionLayout与MotionScene的关系可以用下图表示:
可以看到MotionLayout中有View
、Helper
两部分,其中View部分表示MotionLayout内部的子View,MotionLayout是ConstraintLayout的子类,所以本身也是个ViewGroup;而Helper部分可以认为是动画相关了,即通过MotionLayout内部app:layoutDescription
属性关联的以MotionScene为根元素的XML文件,接下来就看看这个MotionScene的内容。
MotionScene
MotionScene 由三部分组成:ConstraintSet 、Transition、StateSet。我们通过一个示例来分别介绍他们各自的含义,效果图:
先看对应的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<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/motion_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/motion_layout_anim"
app:motionDebug="SHOW_ALL"
app:showPaths="true">
<!--motionDebug取值:1、NO_DEBUG 2、SHOW_ALL 3、SHOW_PATH 4、SHOW_PROGRESS-->
<!--通过layoutDescription来描述运动场景-->
<Button
android:id="@+id/btn_clk"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp"
android:text="点击"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible" />
<TextView
android:id="@+id/ll_pop_dialog"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@color/cffd28c"
android:gravity="center"
android:text="Pop Dialog"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_clk" />
</androidx.constraintlayout.motion.widget.MotionLayout>
布局很简单,id 为 ll_pop_dialog 的 TextView
表示要弹出的内容,也是我们要做动画的部分,但是目前还没看到动画相关的约束,这是因为约束都在另一个声明动画XML的文件中呢 (MotionScene 中声明的 ConstraintSet)。
ConstraintSet
一个完整动画肯定包含动画的起点和终点,而通过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">
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/ll_pop_dialog"
android:layout_width="match_parent"
android:layout_height="200dp"
android:visibility="invisible"
motion:layout_constraintTop_toBottomOf="@+id/btn_clk" />
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/ll_pop_dialog"
android:layout_width="match_parent"
android:layout_height="200dp"
android:visibility="visible"
motion:layout_constraintBottom_toTopOf="@+id/btn_clk">
<!--自定义属性-->
<CustomAttribute
motion:attributeName="backgroundColor"
motion:customColorValue="#D81B60" />
</Constraint>
</ConstraintSet>
//......
</MotionScene>
start表示起点,end表示终点,内部Constraint表示约束信息,通过Id的方式与布局文件中的View建立绑定关系。如上描述,在start起点时,弹窗内容默认在Button按钮的下方;在end终点时,移动到了Button的上方。除了起点和终点,如何让动画动起来呢,这就要用到下面的Transition转换了。
Transition
在Transition中将上述两个ConstraintSet的Id通过constraintSetStart、constraintSetEnd进行设置,触发动画可以通过OnClick、OnSwipe:
<?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:constraintSetEnd="@+id/end"
motion:constraintSetStart="@id/start"
motion:duration="300">
<KeyFrameSet></KeyFrameSet> //可以设置关键帧
<OnClick
motion:clickAction="toggle"
motion:targetId="@+id/btn_clk"/>
</Transition>
//......
</MotionScene>
- constraintSetStart:ConstraintSet对应的Start状态,
android:id="@+id/start"
- constraintSetEnd:ConstraintSet对应的End状态,
android:id="@+id/end"
- duration:动画持续时间。
KeyFrameSet
已经知道ConstraintSet描述了Start、End状态,那么动画过渡的路径可以是直线,也可以是任意曲线,而这就需要通过KeyFrameSet关键帧来设置了。
OnClick
表示用户点击触发转场动画
- targetId:指定布局文件中View的id,当点击此id对应的View时会触发动画;如果不指定id,那么点击整个MotionLayout区域都会触发动画,有点像PopupWindow设置focus=true。
- clickAction:可以设置
transitionToStart、transitionToEnd、toggle、jumpToStart、jumpToEnd
,他们各自的含义如下:
toggle:默认设置,在transitionToStart、transitionToEnd之间来回切换。如果在Start状态,点击之后会切换到End状态,反之一样。
transitionToEnd:过渡到End状态,有动画效果;
transitionToStart:过渡到Start状态,有动画效果;
jumpToEnd:直接跳转到End状态,没有动画效果;
jumpToStart:直接跳转到Start状态,没有动画效果。
OnSwipe
用户滑动触发,它会根据用户滑动行为调整动画的进度。一个Transition 标签下可以包含多个OnSwipe。如:
<OnSwipe
motion:dragDirection="dragEnd"
motion:touchAnchorId="@+id/btn_clk" />
AS控制面板中,start、end上面的箭头就是Transition,那个点指的就是OnClick
如果把OnSwipe加上,代码如下:
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@id/start"
motion:duration="300">
<OnClick
motion:clickAction="toggle"
motion:targetId="@+id/btn_clk" />
<OnSwipe
motion:dragDirection="dragUp"
motion:touchAnchorId="@+id/btn_clk" />
</Transition>
再来看下控制面板,此时又多了个长条,嗯!看来这个长条表示的就是OnSwipe了,这两个形状表示的还挺形象(点是点击,长条表示拖拽!)
资料
【1】Constraintlayout 版本:developer.android.google.cn/jetpack/and… 【2】motionlayout Part I: medium.com/google-deve…