1、前言

最近在开发中,搭档居然对MontionLayout一知半解,那怎么行!百里偷闲写出此文章,一同学习、一同进步。假如写的不好,或许有过错之处,恳请在谈论、私信、邮箱指出,万分感谢

希望你在阅览这篇文章的时分,现已对下面的内容熟练掌握了

  • Animated Vector Drawable
  • Property Animation framework
  • LayoutTransition animations
  • Layout transitions with TransitionManager

对了还有ConstraintLayout必须熟练掌握

对了,假如能够,请跟随敲代码,毕竟你脑补的代码,没有编译器。

当然你也能够阅览相关文章

  • 5分钟带你学会MotionLayou 第一篇
  • 5分钟带你学会MotionLayou 第二篇

2、简介

1)依据功能将MontionLayout视为特点动画结构、TransitionManagerCoordinatorLayout 的混合体。答应描绘两个布局之间的转化(如 TransitionManager),但也能够为任何特点设置动画(不仅仅是布局特点)。

2)支撑可搜索的过渡,如 CoordinatorLayout(过渡能够完全由接触驱动并立即过渡到的任何点)。支撑接触处理和要害帧,答应开发人员依据自己的需求轻松自界说过渡。

3)在这个范围之外,另一个要害差异是 MotionLayout 是完全声明式的——你能够用 XML 完整地描绘一个复杂的转化——不需求代码(假如你需求经过代码来表达运动,现有的特点动画结构现已供给了一种很好的方式正在做)。

4)MotionLayout 只会为其直接子级供给其功能——与 TransitionManager 相反,TransitionManager 能够运用嵌套布局层次结构以及 Activity 转化。

3、何时运用

MotionLayout 设想的场景是当需求移动、调整实践 UI 元素(按钮、标题栏等)或为其设置动画时——用户需求与之交互的元素。

重要的是要知道到运动是有意图的——不应该只是你运用程序中一个无偿的特殊作用;应该用来帮助用户了解运用程序在做什么。Material Design 准则网站很好地介绍了这些概念。

有一类动画只需求处理播映预界说的内容,用户不会——或不需求——直接与内容交互。视频、GIF,或许以有限的方式,动画矢量可绘制对象或lottie文件通常属于此类。MotionLayout 并不专门测验处理此类动画(但当然能够将们包括在 MotionLayout 中)。

4、依赖

确保constraintlayout版别>=2.0.0即可

build.gradle

dependencies {
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
}
//or
dependencies {
    implementation("androidx.constraintlayout:constraintlayout:2.1.3")
}

5、ConstraintSet

假如运用ConstraintLayout并不行多,那对ConstraintSets的知道可能不行完善,咱们也展开说说

ConstraintSets包括了一个或多个束缚联系,每个束缚联系界说了一个视图与其父布局或其他视图之间的方位联系。经过运用ConstraintSets,开发者能够在运转时更改布局的束缚联系,然后实现动画或动态变化的布局作用。

比如ConstraintSets包括了以下办法:

  1. clone():克隆一个ConstraintSet实例。
  2. clear():铲除一切的束缚联系。
  3. connect():衔接一个视图与其父布局或其他视图之间的束缚联系。
  4. center():将一个视图水平或垂直居中于其父布局或其他视图。
  5. create():创立一个新的ConstraintSet实例。
  6. constrain*():束缚一个视图的方位、巨细、宽高比、可见性等特点。
  7. applyTo():将束缚联系运用到一个ConstraintLayout实例。

还有更多办法就不一一列举了

只运用 ConstraintSet 和 TransitionManager 来实现一个平移动画

fragment_motion_01_basic.xml

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cl_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <View
        android:id="@+id/button"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginStart="8dp"
        android:background="@color/orange"
        android:text="Button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
// 界说开端状况的 ConstraintSet (clContainer顶层容器)
val startConstraintSet = ConstraintSet()
startConstraintSet.clone(clContainer)
// 界说完毕状况的 ConstraintSet
val endConstraintSet = ConstraintSet()
endConstraintSet.clone(clContainer)
endConstraintSet.connect(
    R.id.button,
    ConstraintSet.END,
    ConstraintSet.PARENT_ID,
    ConstraintSet.END,
    8.dp
)
endConstraintSet.setHorizontalBias(R.id.button,1f)
clContainer.postDelayed({
    // 在需求履行动画的当地
    TransitionManager.beginDelayedTransition(clContainer)
    // 设置完毕状况的 ConstraintSet
    endConstraintSet.applyTo(clContainer)
}, 1000)

咱们首要运用 ConstraintSet.clone() 办法来创立开端状况的 ConstraintSet。然后,咱们经过 ConstraintSet.clone() 和 ConstraintSet.connect() 办法来创立完毕状况的 ConstraintSet,其中 connect() 办法用于衔接视图到另一个视图或父容器的指定方位。在这儿,咱们将按钮衔接到父容器的右端(左端在布局中现已声明晰),然后使其水平居中。接着咱们运用setHorizontalBias使其水平居右。

在需求履行动画的当地,咱们调用 TransitionManager.beginDelayedTransition() 办法告知系统要开端履行动画。然后,咱们将完毕状况的 ConstraintSet 运用到 MotionLayout 中,然后实现滑润的过渡。

5分钟带你学会MotionLayout

ConstraintSet 的一般思想是它们封装了布局的一切定位规矩;由于您能够运用多个 ConstraintSet,因而您能够即时决定将哪组规矩运用于您的布局,而无需重新创立您的视图——只有它们的方位/尺寸会改动。

MotionLayout 基本上建立在这个想法之上,并进一步扩展了这个概念

6、引证现有布局

在第5点中,咱们新建了一个xml,咱们继续运用,不过需求将androidx.constraintlayout.widget.ConstraintLayout修正为androidx.constraintlayout.motion.widget.MotionLayout

fragment_motion_01_basic.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <View
        android:id="@+id/button"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginStart="8dp"
        android:background="@color/orange"
        android:text="Button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.motion.widget.MotionLayout>

你会得到一个过错

5分钟带你学会MotionLayout

靠着强壮的编辑器,生成一个

5分钟带你学会MotionLayout

你就会得到下面这个和一个新的xml文件

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutDescription="@xml/motion_layout_01_scene">
</androidx.constraintlayout.motion.widget.MotionLayout>

也就是一个MotionScene文件

motion_layout_01_scene.xml

<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
</MotionScene>

这儿咱们先用再新建两个xml,代表开端方位和完毕方位

motion_01_cl_start.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    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/orange"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginStart="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

motion_01_cl_end.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    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/orange"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

修正一下

motion_layout_01_scene.xml

<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:motion="http://schemas.android.com/apk/res-auto">
    <!-- Transition 界说动画过程中的开端状况和完毕状况 -->
    <!-- constraintSetStart 动画开端状况的布局文件引证 -->
    <!-- constraintSetEnd 动画完毕状况的布局文件引证 -->
    <Transition
        motion:constraintSetEnd="@layout/motion_01_cl_end"
        motion:constraintSetStart="@layout/motion_01_cl_start"
        motion:duration="1000">
        <!--OnClick 用于处理用户点击事情 -->
        <!--targetId 设置触发点击事情的组件 -->
        <!--clickAction 设置点击操作的呼应行为,这儿是使动画过渡到完毕状况 -->
        <OnClick
            motion:clickAction="toggle"
            motion:targetId="@+id/button" />
    </Transition>
</MotionScene>

部分解说都在注释中啦。好了 ,运转吧。

5分钟带你学会MotionLayout

这儿的TransitionOnClick咱们先按下不表。

7、独立的 MotionScene

上面的比如中,咱们运用了两个XML+一个原有的布局为基础,进行的修正。最终重用您可能现已具有的布局。MotionLayout 还支撑直接在目录中的 MotionScene 文件中描绘 ConstraintSet res/xml

咱们在res/xml目录中新建一个xml文件

motion_layout_02_scene.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:constraintSetStart="@+id/start"
        motion:constraintSetEnd="@+id/end"
        motion:duration="1000">
        <!--OnClick 用于处理用户点击事情 -->
        <!--targetId 设置触发点击事情的组件 -->
        <!--clickAction 设置点击操作的呼应行为,这儿是使动画过渡到完毕状况 -->
        <OnClick
            motion:clickAction="toggle"
            motion:targetId="@+id/button" />
    </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>

首要,在 <MotionScene> 标签内界说了两个 <ConstraintSet>,别离代表动画的开端状况(start)和完毕状况(end)。每个 <ConstraintSet> 内包括一个 <Constraint>,用于描绘一个界面组件(如按钮或文本框)的特点。

<Transition> 标签中,咱们经过 app:constraintSetStartapp:constraintSetEnd 特点指定了动画的开端和终止状况。在这个简单的示例中,咱们没有插值器等特点,但能够经过增加相应的特点(如 android:durationapp:interpolator 等)来自界说动画作用。

运转一下,相同

5分钟带你学会MotionLayout

7、注意

  1. ConstraintSet 用于替换受影响View的一切现有束缚。
  2. 每个 Constraint 元素应包括要运用于View的一切束缚。
  3. ConstraintSet 不是运用增量束缚,而是铲除并仅运用指定的束缚。
  4. 对于只有一个View需求动画的场景,MotionScene 中的 ConstraintSet 只需包括该View的 Constraint。
  5. 能够看出 MotionScene 界说和之前是相同的,可是咱们将开端和完毕 ConstraintSet 的界说直接放在文件中。与普通布局文件的主要差异在于咱们不指定此处运用的View的类型,而是将束缚作为元素的特点。

8、AndroidStudio预览工具

Android Studio 支撑预览 MotionLayout,能够运用规划形式检查并编辑 MotionLayout

5分钟带你学会MotionLayout

标号意义如下

  1. 点击第一个你能够看到,当前页面的具有ID
    5分钟带你学会MotionLayout
  2. 点击第二个,能够看到开端动画的方位
    5分钟带你学会MotionLayout
  3. 点击第三个,能够看到终止动画的方位
    5分钟带你学会MotionLayout
  4. 第四个,能够操作动画的预览,暂停,播映,加快,拖动,等等。
  5. 而你能够看到途中有一条线,能够运用tools:showPaths="true"敞开

9、补充

今天回过来一看,示例还是少了,我略微加几个

<?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">
    <!--OnClick 用于处理用户点击事情 -->
    <!--targetId 设置触发点击事情的组件 -->
    <!--clickAction 设置点击操作的呼应行为,这儿是使动画过渡到完毕状况 -->
    <OnSwipe
      motion:dragDirection="dragEnd"
      motion:touchAnchorId="@+id/button1"
      motion:touchAnchorSide="end" />
​
  </Transition>
​
  <ConstraintSet android:id="@+id/start">
​
    <Constraint
      android:id="@+id/button1"
      android:layout_width="64dp"
      android:layout_height="64dp"
      android:layout_marginStart="8dp"
      motion:layout_constraintBottom_toTopOf="@id/button2"
      motion:layout_constraintStart_toStartOf="parent"
      motion:layout_constraintTop_toTopOf="parent" />
​
    <Constraint
      android:id="@+id/button2"
      android:layout_width="64dp"
      android:layout_height="64dp"
      android:layout_marginStart="8dp"
      android:alpha="1"
      motion:layout_constraintBottom_toTopOf="@id/button3"
      motion:layout_constraintStart_toStartOf="parent"
      motion:layout_constraintTop_toBottomOf="@id/button1" />
​
    <Constraint
      android:id="@+id/button3"
      android:layout_width="64dp"
      android:layout_height="64dp"
      android:layout_marginStart="8dp"
      android:rotation="0"
      motion:layout_constraintBottom_toTopOf="@id/button4"
      motion:layout_constraintStart_toStartOf="parent"
      motion:layout_constraintTop_toBottomOf="@id/button2" />
​
    <Constraint
      android:id="@+id/button4"
      android:layout_width="64dp"
      android:layout_height="64dp"
      android:layout_marginStart="8dp"
      android:elevation="0dp"
      motion:layout_constraintBottom_toTopOf="@id/button5"
      motion:layout_constraintStart_toStartOf="parent"
      motion:layout_constraintTop_toBottomOf="@id/button3" />
​
    <Constraint
      android:id="@+id/button5"
      android:layout_width="64dp"
      android:layout_height="64dp"
      android:layout_marginStart="8dp"
      android:scaleX="1"
      android:scaleY="1"
      motion:layout_constraintBottom_toBottomOf="parent"
      motion:layout_constraintStart_toStartOf="parent"
      motion:layout_constraintTop_toBottomOf="@id/button4" />
  </ConstraintSet>
​
  <ConstraintSet android:id="@+id/end">
    <Constraint
      android:id="@+id/button1"
      android:layout_width="64dp"
      android:layout_height="64dp"
      android:layout_marginEnd="8dp"
      motion:layout_constraintEnd_toEndOf="parent"
      motion:layout_constraintHorizontal_bias="1"
      motion:layout_constraintBottom_toTopOf="@id/button2"
      motion:layout_constraintStart_toStartOf="parent"
      motion:layout_constraintTop_toTopOf="parent" />
​
    <Constraint
      android:id="@+id/button2"
      android:layout_width="64dp"
      android:layout_height="64dp"
      android:layout_marginStart="8dp"
      android:alpha="0.2"
      motion:layout_constraintBottom_toTopOf="@id/button3"
      motion:layout_constraintHorizontal_bias="1"
      motion:layout_constraintEnd_toEndOf="parent"
      motion:layout_constraintStart_toStartOf="parent"
      motion:layout_constraintTop_toBottomOf="@id/button1" />
​
​
    <Constraint
      android:id="@+id/button3"
      android:layout_width="64dp"
      android:layout_height="64dp"
      motion:layout_constraintHorizontal_bias="1"
      android:layout_marginStart="8dp"
      android:rotation="360"
      motion:layout_constraintBottom_toTopOf="@id/button4"
      motion:layout_constraintEnd_toEndOf="parent"
      motion:layout_constraintStart_toStartOf="parent"
      motion:layout_constraintTop_toBottomOf="@id/button2" />
​
    <Constraint
      android:id="@+id/button4"
      android:layout_width="64dp"
      android:layout_height="64dp"
      android:layout_marginStart="8dp"
      android:elevation="10dp"
      motion:layout_constraintBottom_toTopOf="@id/button5"
      motion:layout_constraintEnd_toEndOf="parent"
      motion:layout_constraintHorizontal_bias="1"
      motion:layout_constraintStart_toStartOf="parent"
      motion:layout_constraintTop_toBottomOf="@id/button3" />
​
    <Constraint
      android:id="@+id/button5"
      android:layout_width="64dp"
      android:layout_height="64dp"
      android:layout_marginStart="8dp"
      android:scaleX="2"
      motion:layout_constraintHorizontal_bias="1"
      android:scaleY="2"
      motion:layout_constraintBottom_toBottomOf="parent"
      motion:layout_constraintEnd_toEndOf="parent"
      motion:layout_constraintStart_toStartOf="parent"
      motion:layout_constraintTop_toBottomOf="@id/button4" />
  </ConstraintSet>
​
</MotionScene>

其余部分就不一一展示了,由于你们肯定都知道啦。作用如下

5分钟带你学会MotionLayout

10、下个华章

由于篇幅原因,咱们先到这,这篇文章,只是了解一下,下一篇咱们将会深化了解各种没有详细讲解的情况。

假如您有任何疑问、对文章写的不满意、发现过错或许有更好的办法,欢迎在谈论、私信或邮件中提出,非常感谢您的支撑。

11、感谢

  1. 校稿:ChatGpt
  2. 文笔优化:ChatGpt