Android | MotionLayout入门级使用教程(一)

2,471 阅读5分钟

MotionLayout 介绍

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

MotionLayout 方式1:在XML布局找到Componet Tree,分别点击2、3位置就可以将根布局的ConstraintLayout转换为MotionLayout了;

方式2:当然,也可以手动修改!☺

若果通过方式1,AS会自动在 res/xml/下帮我们创建一个根元素为MotionScene的xml文件:

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

可以看到MotionLayout中有ViewHelper两部分,其中View部分表示MotionLayout内部的子View,MotionLayout是ConstraintLayout的子类,所以本身也是个ViewGroup;而Helper部分可以认为是动画相关了,即通过MotionLayout内部app:layoutDescription属性关联的以MotionScene为根元素的XML文件,接下来就看看这个MotionScene的内容。

MotionScene

MotionScene

MotionScene 由三部分组成:ConstraintSet 、Transition、StateSet。我们通过一个示例来分别介绍他们各自的含义,效果图: motionlayout效果

先看对应的布局文件:

<?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关键帧来设置了。

KeyFrame

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

OnSwipe

用户滑动触发,它会根据用户滑动行为调整动画的进度。一个Transition 标签下可以包含多个OnSwipe。如:

<OnSwipe 
  motion:dragDirection="dragEnd"
  motion:touchAnchorId="@+id/btn_clk" />

AS控制面板中,start、end上面的箭头就是Transition,那个点指的就是OnClick 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了,这两个形状表示的还挺形象(点是点击,长条表示拖拽!)

OnClick&OnSwipe

资料

【1】Constraintlayout 版本:developer.android.google.cn/jetpack/and… 【2】motionlayout Part I: medium.com/google-deve…