『译』ConstrainLayout 2.0 之 MotionLayout(Part1.1)

96
zonda的地盘
2.1 2018.07.02 15:42* 字数 2661

译者注:文章中的很多链接或资源(如:图片、视频)将尽可能的替换为可以不用科学上网的资源,原文标题和部分内容也略有不同,不过自认为不会影响阅读,否则可直接自行看文章底部的原文。

转载请注明原文地址:https://www.jianshu.com/p/5203cf11d943

前言

MotionLayout 是一个在 ConstraintLayout 2.0 版本库中新增的类,用于帮助 Android 开发者,在他们的 App 中,管理手势以及动画组件。

在 2018 Google IO 大会 What’s New with ConstraintLayout and Android Studio Design Tools 的下半场(大概在视频的 29 分钟左右),对这个库的功能有一个较好的综述。

在这个系列文章我们将揭露,你如何添加这个库到你的 App 里,以及如何运用 MotionLayout 的概念和特性来编写程序。

在这个系列文章的 第一部分 我将揭露 MotionLayout 的基础:

  • MotionLayout 因何而生?
  • 添加 ConstraintLayout 2.0 以及 MotionLayout 到你的工程
  • 运用 MotionLayout
  • ConstraintSets
  • MotionScene
  • 第一个例子:
  • OnSwipe handler
  • 第二个例子:
  • MotionLayout 的属性
  • 小结

你也可以在 ConstraintLayout examples github repository 或者 这里 找到这些例子的源码。

意见反馈

这个库仍处于一个早期的 alpha1 版本,在稳定版本之前可能会存在改动。当 alpha 版本不断迭代,随着用户的反馈,很有可能会改变库的 API 或者命名。对于 ConstraintLayout 组件有任何 Bug 或者特性需求都可以在 issue tracker 跟我们反馈。

欢迎大家反馈——不要犹豫,来骚扰我们吧!

MotionLayout 因何而生?

Android 框架已经提供了好几种在 App 里添加动画的方式:

下面将揭示 MotionLayout 和上面这些已有方案的不同。

MotionLayout,从它的名字就可以知道,首先它是一个布局控件,可以让你安置你的元素。事实上它是 ConstraintLayout 的子类,进而在它的基础上构建了更强大的布局功能。

MotionLayout 作为 连接布局过度和复杂的手势操作之间的桥梁 而生。你可以把它当做介于属性动画框架、TransitionManager 和 CoordinatorLayout 之间的功能集合。

它可以让你描述两个布局的过渡动画(类似 TransitionManager 那样),而且还可以描述任何属性动画(不仅仅是布局属性)。( 译者注:这里的 “描述”,指的就是用 xml 表示 ) 此外,它内置支持联动(原文:seekable transitions),正如 CoordinatorLayout(它可以通过触摸事件,和任意一点产生过渡动画的联动)。它支持触摸操作和关键帧,可以让开发者更容易根据自身需要自定义过渡动画。

MotionLayout 是完全声明式的。

除了上面提到的,MotionLayout 另一个关键的不同点在于它是完全声明式的——你可以完全在 XML 中描述一个复杂的过渡动画——无需任何代码。如果你需要使用代码表达移动手势,现有的属性动画框架已经提供了一个很好的方式实现它。(译者注:言下之意,要用代码实现动画,那就用回属性动画吧)

MotionLayout 辅助工具

我们确实认为,对声明式移动规格的关注将简化创建动画,由此在 Android Studio 中提供了很好的图像化工具。

由于我们正在开发这个工具,目前它是不可用的。一旦这个库到了 stable 或者 beta 版本,这个工具就可以用了。

MotionLayout 动画可视化工具.gif

最后,作为 ConstraintLayout 2.0 的一部分,它可以作为一个支持库,向下兼容到 API 级别 18,也就是JellyBean MR2(译者注:就是我们通常说的 Android 4.3):这意味着它支持当下 95% 的 Android 设备,可以查看 Android 系统实时市场占比 (译者注:需科学上网)

各系统占比截图.png

限制

MotionLayout 将仅仅为它的直接子视图提供上述功能——这点正好和 TransitionManger 不一样,TransitionManager 不仅还可以作用于内嵌布局,还可以作用于 Activity 场景动画。

何时使用它?

当动画 UI 元素和用户产生交互时,我们就可以使用 MotionLayout

认识清楚动画是为什么目标服务这是至关重要的——在你的应用中,它不应该是简单的一个无端特效;它应该能够帮助用户理解你的 App 正在做什么。Material Design 原则之 Understanding motion很好的介绍了这些理念。

有一个动画类仅仅要处理演示预先的内容,用户不会或者不需要直接和内容发生交互。一段视频,一个动图,或者以有限的方式比如动画矢量或者 lottie 通常就属于这一范畴。MotionLayout 不会特别的尝试处理这类动画(但是你当然可以把他们包含在一个 MotionLayout 中)

添加 MotionLayout 到你的工程

简单的通过添加 ConstraintLayout 2.0 到你的 Gradle 配置文件就可以了。

dependencies {
    implementation 'com.android.support.constraint:constraint-layout:2.0.0-alpha1'
}

运用 MotionLayout

MotionLayout 是 ConstraintLayout 的一个子类——这样,你可以把它视为一个普通的布局。现有的 ConstraintLayout 转换为 MotionLayout 是很容易的,只要像下面这样替换类名:

<android.support.constraint.ConstraintLayout .../>

改成

<android.support.constraint.motion.MotionLayout .../>
MotionLayout架构图.png

ConstraintLayout 和 MotionLayout 最主要的差异在于 XML 级别(_译者注:使用 MotionLayout 就可以知道,在 xml 中它有一个 app:layoutDescription 属性),MotionLayout 的子视图布局属性,并不一定要包含在布局文件中。

当然,MotionLayout 通常子视图布局属性放到一个分开的 xml 文件中(这就是一个 MotionScene)然后引用它,而且定义在此处的子视图布局属性将优先于布局文件的子视图布局属性。

这种方式,布局文件仅仅只包含自视图和它们的属性,而不包括它们的位置和运动。

ConstraintSets

如果你没有在 ConstraintLayout 中使用过 ConstraintSets,可能需要花几分钟看看这个短视频:

ConstraintSet 大体的思想是,它们封装所有的定位规则为你的布局;并且你能够使用多个 ConstraintSet,然后你可以决定这一组规则立即运用于你的布局,而不需要重新创建你的视图——仅仅只是改变它们的位置或者尺寸。

结合 TransitionManager(若无法科学上网可参考这里),这个提供了一个相对容易的方式使用 ConstraintLayout 创建动画,正如上面视频讲述的那样。

MotionLayout 本质上是构建于这些思想之上,同时扩展这些概念进一步发展。

MotionScene

如之前提到的那样,和通常的布局控件不同,MotionLayout 的一些规格保存在另外一个 XML 文件中,它就是一个 MotionScene,存放于的 res/xml 资源目录下。


MotionScene 的规格组成.png

一个 MotionScene 文件能够包含所有下面指定的动画:

  • 使用 ConstraintSets
  • 这些 ConstraintSets 之间的过度动画
  • 关键帧,触摸操作,等等。

例如,让我们尝试实现,使用你的手指能够拖拽移动一个视图,从屏幕的一端到另一端。


First animation.gif

示例01:引用存在的布局

使用 ConstraintLayout,你将要创建两个 ConstraintSet —— 一个是第一个位置(位于控件在屏幕的左边),一个第二个位置(位于控件在屏幕的右边)。

然后使用 TransitionManager 可以实现动画。但是使用这种方式有一个问题,那就是,一旦过渡动画开始,它就无法中断。也就是说,你不能够让系统跳转到过渡动画指定的时间点——意味着你不能通过用户输入驱动过渡动画。

译者注:上面两段翻译本人闲写得太啰嗦了,就粗略的翻译了原文,作者的基本意图就是引出下面使用 MotionLayout 比上面说的原有 ConstraintSet 方式更简单有效,且使用类似 TransitionManger 的方式并不能够实现用户驱动的动画。

MotionLayout 就解决了所有的这些问题。下面展示你如何实现同样的动画,只要重用这些已经存在的布局,来初始化两个状态。
首先,我们将创建一个 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>

注意,这个布局文件引用了一个 MotionScene 文件 —— scene_01

下面就是 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 来处理这个过渡动画。

好!就这样,你就能实现上面提到的动画了。

OnSwipe handler

回到上面提到的 scene_01.xml 文件,我们指定了一个 OnSwipe 处理者,被包含在 Transition 里。这个处理者的角色是让你驱动这个 Transition,通过匹配你的手指运动。


OnSwipe 属性.png

有些参数需要你来设置:

  • touchAnchorId: 我们需要追踪的对象(这个例子中就是,@+id/button)
  • touchAnchorSide: 需要追踪你手指运动的对象边界(right/left/top/bottom)
  • dragDirection: 我们正追踪的运动方向(dragRight / dragLeft / dragUp / dragDown 可以被设置通过定义进度值,从 0 到 1)

译者注:下面就不再继续详细翻译了,基本就是对 ConstraintLayout examples github repository 例子的代码讲解,而且蛮啰嗦的,不如看代码或者自行看原文吧。

不过,可以参照这些文章标题,作为阅读代码时的大纲:

  • 自定义属性,图片过渡动画,关键帧(part II)
  • MotionLayout 带来的优势(CoordinatorLayout, DrawerLayout, ViewPager)(part III)
  • All about Keyframes! (part IV)
  • MotionLayout as a choreographer of root layout
  • Nesting MotionLayout & other Views
  • MotionLayout with fragments

译一义