[转]MontionLayout:打开动画新世界大门

2024-02-29 05:20

本文主要是介绍[转]MontionLayout:打开动画新世界大门,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转载自:https://juejin.im/post/5d595328f265da03c34bfa59

最初接触到 MotionLayout 是在国外知名博客的 Android 专栏上。第一眼见到 MotionLayout 时无疑是兴奋的,在经过使用和熟悉了这个布局组件之后,我就想将这份喜悦传递给国内开发者,从此“拳打”设计,“脚踢”产品?。当然,由于关于 MotionLayout 的外文专栏相关介绍已足够详细,所以本文仅对其进行总结和简单应用。老规矩,正文开始前先上一张图:

简介

由于本文的受众需要有一点 ConstraintLayout 的用法基础,如果你对它并不熟悉,可以先去花几分钟看一下本人之前的译文:带你领略 ConstraintLayout 1.1 的新功能。回到正题,什么是 MontionLayout ?很多人可能会对这个名词比较陌生,但如果说到它的前身 — ConstraintLayout,大家应该就多少有些了解了。MontionLayout 其实是 Google 在去年开发者大会上新推的布局组件。我们先来看看 Android 官方对于它的定义:

MotionLayout is a layout type that helps you manage motion and widget animation in your app. MotionLayout is a subclass of ConstraintLayout and builds upon its rich layout capabilities.

简单翻译过来就是:MontionLayout 是一个能够帮助我们在 app 中管理手势和控件动画的布局组件。它是 ConstraintLayout 的子类并且基于它自身丰富的布局功能来进行构建。

当然,你也可以按照字面意思将它简单理解为“运动布局”。为什么这么说呢?通过上图来对比传统的布局组件(如:FrameLayoutLinearLayout 等),我们不难发现:MotionLayout 是布局组件中的一个“里程碑”,由此开始就告别了 XML 文件中只能”静态“操作 UI 的历史。通过 MotionLayout,我们就能更加轻易处理其内部子 View 的手势操作和"运动"效果了。正如 Nicolas Roard 所说的那样:

你可以在 MontionLayout 功能方面将其看作是属性动画、TransitionManager 和 CoordinatorLayout 的结合体

MotionLayout 基础

首先,我们需要从 MotionLayout 的一些基本属性和用法讲起,这样对于我们后面的实际操作将会很有帮助。

引入 MotionLayout 库

dependencies {implementation 'com.android.support.constraint:constraint-layout:2.0.0-beta2'
}
复制代码

目前,MotionLayout 仍处于 beta 版本,虽然官方之前说过 MotionLayout 的动画辅助工具将会在 beta 版本推出,但目前还没有出现,不出意外应该是在下一个版本了。到时候应该就可以像 ConstraintLayout 那样直接通过布局编辑器来进行部分预览和参数操作了。

在布局文件中使用 MotionLayout

想要使用 MotionLayout,只需要在布局文件中作如下声明即可:

<android.support.constraint.motion.MotionLayoutxmlns: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"app:layoutDescription="@xml/scene1"></android.support.constraint.motion.MotionLayout>
复制代码

由于 MotionLayout 作为 ConstraintLayout 的子类,那么就自然而然地可以像 ConstraintLayout 那样使用去“约束”子视图了,不过这可就有点“大材小用了”,MotionLayout 的用处可远不止这些。我们先来看看 MotionLayout 的构成:

由上图可知,MotionLayout 可分为 <View><Helper> 两个部分。<View> 部分可简单理解为一个 ConstraintLayout,至于 <Helper> 其实就是我们的“动画层”了。MotionLayout 为我们提供了 layoutDescription 属性,我们需要为它传入一个 MotionScene 包裹的 XML 文件,想要实现动画交互,就必须通过这个“媒介”来连接。

MotionScene:传说中的“百宝袋”

什么是 MotionScene?结合上图 MotionScene 主要由三部分组成:StateSetConstraintSetTransition。为了让大家快速理解和使用 MotionScene,本文将重点讲解 ConstarintSetTransition,至于 StateSet 状态管理将会在后续文章中为大家介绍具体用法和场景。同时,为了帮助大家理解,此处将开始结合一些具体小实例来帮助大家快速理解和使用它。

首先,我们从实现下面这个简单的效果讲起:

GIF 画质有点渣,见谅,但从上图我们可以发现这是一个简单的平移动画,通过点击自身(篮球)来触发,让我们来通过 MotionLayout 的方式来实现它。首先来看下布局文件:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.motion.MotionLayoutxmlns: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"app:layoutDescription="@xml/step1"tools:context=".practice.MotionSampleActivity"><ImageViewandroid:id="@+id/ball"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/ic_basketball"/>
</android.support.constraint.motion.MotionLayout>
复制代码

布局文件很简单,只不过你可能会注意到,我们对 ImageView 并没有添加任何约束,原因在于:我们会在 MotionScene 中声明 ConstraintSet,里面将包含该 ImageView 的“运动”起始点和终点的约束信息。当然你也可以在布局文件中对其加以约束,MotionScene 中对于控件约束的优先级会高于布局文件中的设定。这里我们通过 layoutDescription 来为 MotionLayout 设置它的 MotionScenestep1,接下来就让我们一睹 MotionScene 的芳容:

<?xml version="1.0" encoding="utf-8"?>
<!--describe the animation for activity_motion_sample_step1.xml-->
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><!-- A transition describes an animation via start and end state --><Transitionapp:constraintSetStart="@id/start"app:constraintSetEnd="@id/end"app:duration="2200"><OnClickapp:targetId="@id/ball"app:clickAction="toggle" /></Transition><!-- Constraints to apply at the start of the animation --><ConstraintSet android:id="@+id/start"><Constraintandroid:id="@+id/ball"android:layout_width="48dp"android:layout_height="48dp"android:layout_marginStart="12dp"android:layout_marginTop="12dp"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"/></ConstraintSet><!-- Constraints to apply at the end of the animation --><ConstraintSet android:id="@+id/end"><Constraintandroid:id="@+id/ball"android:layout_width="48dp"android:layout_height="48dp"android:layout_marginEnd="12dp"android:layout_marginBottom="12dp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintBottom_toBottomOf="parent"/></ConstraintSet></MotionScene>
复制代码

首先,可以发现我们定义了两个 <ConstraintSet>,分别描述了这个? ImageView 的动画起始位置以及结束位置的约束信息(仅包含少量必要信息,如:width、height、margin以及位置属性等)。显而易见,篮球的起始位置为屏幕左上角,结束位置为屏幕右下角,那么问题来了,如何让它动起来呢?这就要依靠我们的 <Transition> 元素了。事实上,我们都知道,动画都是有开始位置和结束位置的,而 MotionLayout 正是利用这一客观事实,将首尾位置和动画过程分离,两个点位置和距离虽然是固定的,但是它们之间的 Path 是无限的,可以是“一马平川”,也可以是"蜿蜒曲折"的。

回到上面这个例子,我们只需要为 Transition 设置起始位置和结束位置的 ConstraintSet 并设置动画时间即可,剩下的都交给 MotionLayout 自动去帮我们完成。当然你也可以通过 onClick 点击事件来触发动画,绑定目标控件的 id 以及通过 clickAction 属性来设置点击事件的类型,这里我们设置的是 toggle,即通过反复点击控件来切换动画的状态,其他还有很多属性可以参照官方文档去研究,比较简单,这里就不一一讲解它们的效果了。如此一来,运行一下就能看到上面的效果了。另外,为了方便测试,我们可以给 MotionLayout 加上调试属性:app:motionDebug="SHOW_PATH",然后就能轻易的查看其动画内部的运动轨迹:

什么?你说这个动画效果太基础?那好,我就来个简陋版的“百花齐放”效果吧,比如下面这样:

首先,让我们分析一下这个效果:仔细看我们可以发现,通过向上滑动蓝色的 Android 机器人,紫色和橙色的机器人会慢慢淡出并分别忘左上角和右上角移动。布局文件很简单,一把梭就OK了?:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.motion.MotionLayoutxmlns: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"app:motionDebug="SHOW_PATH"app:layoutDescription="@xml/step2"tools:context=".practice.MotionSampleActivity"><ImageViewandroid:id="@+id/ic_android_blue"android:layout_width="42dp"android:layout_height="42dp"android:src="@mipmap/android_icon_blue"/><ImageViewandroid:id="@+id/ic_android_left"android:layout_width="42dp"android:layout_height="42dp"android:src="@mipmap/android_icon_purple"/><ImageViewandroid:id="@+id/ic_android_right"android:layout_width="42dp"android:layout_height="42dp"android:src="@mipmap/android_icon_orange"/><TextViewandroid:id="@+id/tipText"android:text="Swipe the blue android icon up"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintEnd_toEndOf="parent"android:layout_marginEnd="16dp"android:layout_marginTop="16dp"app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.motion.MotionLayout>
复制代码

下面我们来看下 step2 中的 MotionScene:

<?xml version="1.0" encoding="utf-8"?>
<!--describe the animation for activity_motion_sample_step2.xml-->
<!--animate by dragging target view-->
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><!--At the start, all three stars are centered at the bottom of the screen.--><ConstraintSet android:id="@+id/start"><Constraintandroid:id="@+id/ic_android_blue"android:layout_width="42dp"android:layout_height="42dp"android:layout_marginBottom="20dp"app:layout_constraintStart_toStartOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintBottom_toBottomOf="parent"/><Constraintandroid:id="@+id/ic_android_left"android:layout_width="42dp"android:layout_height="42dp"android:alpha="0.0"android:layout_marginBottom="20dp"app:layout_constraintStart_toStartOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintBottom_toBottomOf="parent"/><Constraintandroid:id="@+id/ic_android_right"android:layout_width="42dp"android:layout_height="42dp"android:layout_marginBottom="20dp"android:alpha="0.0"app:layout_constraintStart_toStartOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintBottom_toBottomOf="parent"/></ConstraintSet><!--Define the end constraint to set use a chain to position all three stars together below @id/tipText.--><ConstraintSet android:id="@+id/end"><Constraintandroid:id="@+id/ic_android_left"android:layout_width="58dp"android:layout_height="58dp"android:layout_marginEnd="90dp"android:alpha="1.0"app:layout_constraintHorizontal_chainStyle="packed"app:layout_constraintStart_toStartOf="parent"app:layout_constraintEnd_toStartOf="@id/ic_android_blue"app:layout_constraintTop_toBottomOf="@id/tipText"/><Constraintandroid:id="@+id/ic_android_blue"android:layout_width="58dp"android:layout_height="58dp"app:layout_constraintEnd_toStartOf="@id/ic_android_right"app:layout_constraintStart_toEndOf="@id/ic_android_left"app:layout_constraintTop_toBottomOf="@id/tipText"/><Constraintandroid:id="@+id/ic_android_right"android:layout_width="58dp"android:layout_height="58dp"android:layout_marginStart="90dp"android:alpha="1.0"app:layout_constraintStart_toEndOf="@id/ic_android_blue"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintTop_toBottomOf="@id/tipText"/></ConstraintSet><!-- A transition describes an animation via start and end state --><Transitionapp:constraintSetStart="@id/start"app:constraintSetEnd="@id/end"><!-- MotionLayout will track swipes relative to this view --><OnSwipe app:touchAnchorId="@id/ic_android_blue"/></Transition>
</MotionScene>
复制代码

上面代码其实很好理解,之前我们定义了一个控件的 Constraint,现在只需要多加两个即可。由于三个 Android 机器人起点位置是一样的,而只有蓝色的显示,那么只要在开始位置将另外的两个机器人透明度设置为 0 即可,然后在结束位置将三个小机器人分开摆放,这里设计到 ConstraintLayout 的基础,就不多说了。接着将结束位置的左、右 Android 机器人透明度设置为 1,动画开始后,MotionLayout 会自动处理目标控件 alpha 属性的变化效果,让其看起来依旧丝滑。

另外,我们这里没有再通过 <OnClick> 来触发动画效果,类似的,我们使用了 <OnSwipe> 手势滑动来触发动画,只需要指定 touchAnchorId 为蓝色小机器人即可,怎么样,是不是有种“拍案惊奇”的感觉?。此外,你可以通过指定 touchAnchorSidedragDirection 等来指定自己想要的滑动手势和滑动方向,默认为向上滑动,手势滑动我们将在后面示例中穿插使用和讲解,这里不做具体介绍,忍不住的小伙伴可以去查看一下官方文档介绍。OK,就这样,我们上面的伪“百花齐放”效果就已经实现了,没什么难的对吧?。

到这里,你可能会说:前面两个示例的动画轨迹一直是"直线",如果想要某段动画过程的轨迹是"曲线"效果可以吗?当然没问题!Keyframes 关键帧帮你安排!

KeyFrameSet:让动画独树一帜

如果我们想实现“独树一帜”的动画交互效果,那就离不开 KeyFrameSet 这个强大的属性。它可以改变我们动画过程中某个关键帧的位置以及状态信息。这样说可能不太好理解,我们先来看下面这个示例:

以大家的慧眼不难发现:风车的运动轨迹为曲线,并且旋转并放大至中间位置时会达到零界点,然后开始缩小。布局代码就不上了,很简单,里面唯一重要的就是我们需要实现的 MontionScene 效果 — step3.xml 了:

<?xml version="1.0" encoding="utf-8"?>
<!--describe the animation for activity_motion_sample_step3.xml-->
<!--animate in the path way on a view-->
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><!-- Constraints to apply at the start of the animation --><ConstraintSet android:id="@+id/start"><Constraintandroid:id="@id/windmill"android:layout_width="40dp"android:layout_height="40dp"android:layout_marginStart="12dp"android:layout_marginBottom="12dp"app:layout_constraintStart_toStartOf="parent"app:layout_constraintBottom_toBottomOf="parent"/><Constraintandroid:id="@id/tipText"android:layout_width="wrap_content"android:layout_height="wrap_content"android:alpha="0.0"app:layout_constraintStart_toStartOf="parent"app:layout_constraintBottom_toBottomOf="@id/windmill"app:layout_constraintTop_toTopOf="@id/windmill"/></ConstraintSet><!-- Constraints to apply at the end of the animation --><ConstraintSet android:id="@+id/end"><!--this view end point should be at bottom of parent--><Constraintandroid:id="@id/windmill"android:layout_width="40dp"android:layout_height="40dp"android:layout_marginBottom="12dp"android:layout_marginEnd="12dp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintBottom_toBottomOf="parent"/><Constraintandroid:id="@+id/tipText"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="12dp"android:alpha="1.0"android:layout_marginEnd="72dp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintBottom_toBottomOf="parent"/></ConstraintSet><!-- A transition describes an animation via start and end state --><Transitionapp:constraintSetStart="@id/start"app:constraintSetEnd="@id/end"><KeyFrameSet><KeyPositionapp:framePosition="50"app:motionTarget="@id/windmill"app:keyPositionType="parentRelative"app:percentY="0.5"/><!--apply other animation attributes--><!--前半段的动画效果:逆时针旋转一圈,同时放大一倍--><KeyAttributeapp:motionTarget="@id/windmill"android:rotation="-360"android:scaleX="2.0"android:scaleY="2.0"app:framePosition="50"/><!--后半段的动画效果:逆时针旋转一圈,同时变回原样--><KeyAttributeapp:motionTarget="@id/windmill"android:rotation="-720"app:framePosition="100"/><!--延迟动画——0-85过程中将透明度一直维持在0.0--><KeyAttributeapp:motionTarget="@id/tipText"app:framePosition="85"android:alpha="0.0"/></KeyFrameSet><OnSwipeapp:touchAnchorId="@id/windmill"app:touchAnchorSide="bottom"app:dragDirection="dragRight"/></Transition></MotionScene>
复制代码

从上述代码我们可以发现:KeyFrameSet 需要被包含在 Transition 里面,同时 KeyFrameSet 中定义了 <KeyPosition><KeyAttribute> 两种元素,它们主要用来设置动画某个位置的关键帧,进而为某段动画指定所期望的效果。顾名思义,KeyPosition 用于指定动画某个关键帧的位置信息,而 KeyAttribute 则用来描述动画某关键帧的属性配置(如:透明度、缩放、旋转等)。除此以外,KeyFrameSet 中还支持 <KeyCycle><KeyTimeCycle> 来让动画变得更加有趣和灵活,因篇幅有限,将在后续文章对二者进行讲解。

从上图可见,keyPositionType 一共有三种,本文使用的是 parentRelative,即以整个 MotionLayout 的布局为坐标系,左上角为坐标原点,即参考 View 的坐标系即可,而另外两种将在后续文章统一讲解和应用,它们的区别在于坐标系选取的参考点不同而已。我们通过 framePosition 属性来指定关键帧所在的位置,取值范围为 0 - 100,本示例中设置的 50 即为动画中点位置。另外,可以通过指定 percentXpercentY 来设置该关键帧位置的偏移量,它们取值一般为 0 — 1,当然也可以设置为负数或者大于一,比如,本示例中如果没有设置偏移量,那么动画的轨迹无疑是一条平行于 x 轴的直线,但通过设置 app:percentY="0.5",那么风车就会在动画中点位置向 y 轴方向偏移一半的高度,即下图的效果(开始 debug 模式):

可能会有人问了:为什么轨迹不是三角形,而是曲线呢?哈哈,这个问题问得好!因为 MotionLayout 会自动地将关键帧位置尽量衔接的圆滑,让动画执行起来不那么僵硬。其他代码应该就比较好理解了,可以参照文档理解。
 

 

 

这篇关于[转]MontionLayout:打开动画新世界大门的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/757688

相关文章

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

用Unity2D制作一个人物,实现移动、跳起、人物静止和动起来时的动画:中(人物移动、跳起、静止动作)

上回我们学到创建一个地形和一个人物,今天我们实现一下人物实现移动和跳起,依次点击,我们准备创建一个C#文件 创建好我们点击进去,就会跳转到我们的Vision Studio,然后输入这些代码 using UnityEngine;public class Move : MonoBehaviour // 定义一个名为Move的类,继承自MonoBehaviour{private Rigidbo

ORACLE 11g 创建数据库时 Enterprise Manager配置失败的解决办法 无法打开OEM的解决办法

在win7 64位系统下安装oracle11g,在使用Database configuration Assistant创建数据库时,在创建到85%的时候报错,错误如下: 解决办法: 在listener.ora中增加对BlueAeri-PC或ip地址的侦听,具体步骤如下: 1.启动Net Manager,在“监听程序”--Listener下添加一个地址,主机名写计

动画AnimationDrawable、转动

现实开发中:很多地方都用到 点击动画的特效; 本案例本人做了三个关于“动” 画 的效果; 先上图: 总体图: A: B: 1:点击图片按钮,效果是:图片闪动; 通过在xml中定义:标签:animation-list来实现点击动画的效果;  是否循环标签:oneshot ;   时间间隔标签:duration ; 要显示的图片标签:drawable ;

13 transition数组的动画使用

划重点 动画:transitiontransition-group :数组动画数组的 添加 / 删除 豆腐粉丝汤 清淡又健康 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><me

12 动画transition的使用2

划重点 Vue 动画:transition / transform在动画周期中执行动动画(上一篇是通过动画样式控制动画) 清蒸扇贝 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><

搜狗浏览器打开CSDN博客排版错乱问题解决

之前发生过几次,不知道什么原因。 今天一直用着好好的,打开一个csdn连接,显示404,博文被删除了,于是就用百度快照打开试试,百度快照打开显示的排版很乱也没找到有用信息。 后面再浏览CSDN博客就排版错乱,显示一个大大二维码图片。 尝试删除IE缓存无效,使用谷歌浏览是好的。 基本锁定就是搜狗缓存导致的,于是找如何删除搜狗缓存   清除后恢复正常

我成功在本地打开了Cesium啦!

1首先下载Node.js,我是跟着这篇下载的,https://zhuanlan.zhihu.com/p/77594251,不过这后面的我没弄对Cesium环境配置也没影响。 另外:我看其他推文说,在终端写node -v和npm-v查node和npm的版本可以检测node和npm是否下载成功。 2然后我在CesiumB站官号看的教学视频,跟着下载Cesium源代码。 Cesium基础入门1-零

PageOfficeCtrl支持直接打开服务器磁盘文件

一般来说,PageOfficeCtrl控件的WebOpen方法的第一个参数是待打开文档的URL,此URL可以是相对于当前页面的相对URL,也可以是相对于整个网站根的相对URL,还可以是http开头的完整URL,但是这个URL必须是当前网站的URL,不能跨域。 现在为了更加方便开发者编程,WebOpen支持打开服务器磁盘文件。也就是说,第一个参数可以写成服务器文件的绝对磁盘路径。例如: P