Android MotionLayout相关

1、概述

I / O '18提到了MotionLayout,当时还没有正式发布前段时间,在今年的6月26日正式发布了ConstraintLayout的2.0alpha版,也算正式推出了MotionLayout。 MotionLayout是ConstraintLayout的子类,它具有ConstraintLayout的所有属性。MotionLayout用来处理两个ConstraintSet之间的切换,并在根据两个ConstraintSet的CustomAttribute参数来自动生成切换动画,关于ConstraintSet下面会讨论。同时MotionLayout所增加的是可以直接通过触摸屏幕来控制动画的运行进度。也就是说MotionLayout会管理你的触摸事件通过跟踪手指的速度,并将其与系统中的视图速度相匹配。从而可以自然地在两者之间通过触摸滑动平稳过渡。并且在动画里面加入了关键帧的概念,使得其自动生成动画在运行时某一阶段会运行到关键帧的状态。同时MotionLayout支持在XML中完全描述一个复杂的动画,而不需要通过Java代码来实现。

ConstraintSet之间切换

2、ConstraintLayout动画

ConstraintLayout中的动画要借助于ConstraintSet。ConstraintSet是一个轻量级对象,表示ConstraintLayout中所有子元素的constraints,margins和padding 。当将 ConstraintSet应用于显示ConstraintLayout时,布局会使用ConstraintSet中的约束来更新对应ConstraintLayout中的。ConstraintSet仅为视图的大小和位置设置动画,不会为其他属性设置动画(例如颜色)。

下面的代码示例显示了如何动画将单个按钮移动到屏幕底部:


public class MainActivity extends AppCompatActivity {

    ConstraintLayout constraintLayout;
    Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.keyframe_one);
        constraintLayout = findViewById(R.id.constraint_layout);

        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                animateToKeyframeTwo();
            }
        });
    }

    void animateToKeyframeTwo() {

        ConstraintSet constraintSet = new ConstraintSet();
        constraintSet.load(this, R.layout.keyframe_two); //载入要更新的布局到constraintSet中
        TransitionManager.beginDelayedTransition(constraintLayout); // 开启
        constraintSet.applyTo(constraintLayout);
    }
}


ConstraintSet动画

这边要注意一下,只会运行R.layout.keyframe_two中与R.layout.keyframe_one中id对应的动画。不会出现R.layout.keyframe_two中有而R.layout.keyframe_one中没有的视图,也不会对R.layout.keyframe_one有但R.layout.keyframe_two中没有的视图有任何效果。

3、MotionLayout动画

前面已经演示了怎么对ConstraintLayout布局设置动画,现在来讨论下MotionLayout布局下的动画。

3.1 MotionScene

与通常的布局不同,MotionLayout所做的约束保存在一个单独的XML文件MotionScene中,该文件存储在您的res/xml目录中。

MotionScene包含内容

MotionScene文件可以包含指定动画所需的全部内容,例如前面提到的ConstraintSets、ConstraintSets直接的过渡、关键帧、触摸处理等等。

3.2 创建动画简单流程

3.2.1 先决条件

① Android Studio 3.2.0或更高版本

② 运行Android API等级21或更高版本的设备或模拟器

③ 添加依赖:


implementation 'com.android.support:appcompat-v7:27.0.2'

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

或者


implementation 'androidx.appcompat:appcompat:1.0.0-beta1'

implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha1'

这边要说明一下androidx也是今年IO刚刚推出的一个依赖,用来替代之前的com.android.support依赖。添加了它就不用添加一大堆v4、v7、v13等依赖了。

3.2.2 定义布局

前面提到过MotionLayout是ConstraintLayout的子类,所以MotionLayout可以直接替换ConstraintLayout。因为ConstraintLayout有的功能MotionLayout都有。

3.2.3 创建MotionScene

这一步是MotionLayout的关键,在res下的xml文件夹中创建MotionScene。其实在MotionLayout中可以不用添加想进行动画的视图的约束,而将约束放在ConstraintSet中,在将ConstraintSet放在MotionScene中。


<MotionScene

    xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:app="http://schemas.android.com/apk/res-auto">

    <ConstraintSet android:id="@+id/starting_set">

        <Constraint android:id="@+id/actor"

            app:layout_constraintBottom_toBottomOf="parent"

            app:layout_constraintRight_toRightOf="parent"

            android:layout_width="60dp"

            android:layout_height="60dp"/>

    </ConstraintSet >

    <ConstraintSet  android:id="@+id/ending_set" >

        <Constraint android:id="@+id/actor"

            app:layout_constraintTop_toTopOf="parent"

            app:layout_constraintLeft_toLeftOf="parent"

            android:layout_width="60dp"

            android:layout_height="60dp"/>

    </ConstraintSet >

</MotionScene>

这边需要注意的是每个ConstraintSet 里面的元素必须始终指定所需的位置和所需的大小。它会覆盖任何以前设置的布局信息。并且里面的id和MotionLayout中的视图的id要对应才会有反应。

为了帮助MotionLayout 的视图理解必须约束集的顺序,需要创建一个Transition 元素。通过使用其直观命名 constraintSetStart 和constraintSetEnd 属性,可以指定首先应用哪个集合以及最后应用哪个集合。该Transition 元素还允许指定动画的持续时间。


// 放在上面的<MotionScene> 和</MotionScene>之中和ConstraintSet 标签平级。

<Transition

    android:id="@+id/my_transition"

    app:constraintSetStart="@+id/starting_set"

    app:constraintSetEnd="@+id/ending_set"

    app:duration="2000"/>

此时,一个简单的MotionScene完成。但是此时任然没有和MotionLayout进行绑定。需要给MotionLayout添加app:layoutDescription属性来将上面的MotionScene绑定:


app:layoutDescription="@xml/my_scene"

3.2.4 启动动画

运行应用程序时,MotionLayout 视图将自动将constraintSetStart 属性中指定的约束集设置到自己身上。因此,要启动动画,需要做的就是调用transitionToEnd() 方法从而实现ConstraintSet之间的转换:


motion_container.transitionToEnd();

动画效果

3.2.5 动画执行进度监听

可以通过给MotionLayout 设置监听器来监听动画进度,和动画完成时的回调:


motionLayout.setTransitionListener(new MotionLayout.TransitionListener() {

            @Override

            public void onTransitionChange(MotionLayout motionLayout, int i, int i1, float v) {

                seekBar.setProgress((int)(v*100));

            }

            @Override

            public void onTransitionCompleted(MotionLayout motionLayout, int i)             {

            }

});

上面对进度的监听通过seekbar表示出来

动画进度监听

3.3 关键帧(Key Frames)

在上面的动画中,Button小部件看起来像在直线的路径中移动。这是因为MotionLayout 的视图此时其实只有两个关键帧:起始帧Button位于屏幕的右下角,终点帧Button位于屏幕的左上角。如果要改变路径的形状,则必须提供一些介于起点和终点之间关键。

在开始创建关键帧之前,必须将KeyFrameSet 标签添加到MotionScene之中。可以自由创建任意数量的关键帧。


<KeyFrameSet >

            ...

</KeyFrameSet >

MotionLayout 视图支持许多不同类型的关键帧。这里使用其中两种类型:KeyPosition 和KeyCycle 。

3.3.1 KeyPosition

KeyPosition 可以帮助视图改变运动路径的形状。创建它们时,请确保提供目标视图的ID,沿时间轴的位置,可以是0到100之间的任意数字,以及指定X或Y坐标已经运行到的百分比。可以设置type参数指出坐标是相对于实际的X或Y轴,还是相对于路径本身。


<KeyFrameSet >

    <KeyPosition

    app:target="@+id/button"

    app:framePosition="30"

    app:type="deltaRelative"

    app:percentX="0.85"/>

    <KeyPosition

    app:target="@+id/button"

    app:framePosition="60"

    app:type="deltaRelative"

    app:percentX="1"/>

</KeyFrameSet>

上面第一个KeyPosition代表button按钮在运行道30%的时候,相对于运行轨迹x已经运行了85%了。第二个KeyPosition代表button按钮在运行道60%的时候,相对于运行轨迹x已经运行了100%了.效果如下,这样就可以避开和seekbar的冲突了:

KeyPosition关键帧

3.3.2 KeyCycle

KeyCycle用来给动画添加振动。可以通过提供诸如要使用的波形和波形周期等详细信息来配置KeyCycle。下面是KeyCycle支持的各种振动波形:

KeyCycle波形

在上述动画中加入如下KeyCycle


<KeyCycle

    app:target="@+id/button"

    app:framePosition="30"

    android:rotation="50"

    app:waveShape="sin"

    app:wavePeriod="1"/>

KeyCycle关键帧

3.4 交互式动画

上面的动画运行我都是通过对Button按钮设置点击监听事件,然后调用motion_container.transitionToEnd();方法来使他运行的。其实完全不必这么麻烦,因为MotionLayout的视图允许开发者将触摸事件直接附加到视图中。截止到现在,它支持点击和滑动事件。要实现上面实现的点击事件可以在MotionScene中增加代码如下:


<OnClick

    app:target="@+id/button"

    app:mode="transitionToEnd"/>

而可以通过给MotionScene增加OnSwipe标签来使视图通过在屏幕滑动而大运行。在创建该标签时,必须确保提供正确的拖动方向以及应作为拖动控制柄的视图的边。可以这么理解,相对于初始位置,如果想往上滑起到增加动画进度就设置为dragUp,想往下滑起到增加动画进度就设置为dragDown,左右同样道理。至于touchAnchorSide这个参数的本意应该设置拉目标视图的边,但我发现就算不设置touchAnchorSide这个参数或者设置成任意值top bottom或者left right,对动画都没有影响。这可能是MotionLayout的一个bug毕竟现在还只是alpha版。


<OnSwipe

    app:touchAnchorId="@+id/actor"

    app:dragDirection="dragUp"/>

OnSwipe动画

5、MotionEditor

之前一篇讨论ConstraintLayout的文章,基本上都是在布局编辑器中进行操作。这也是ConstraintLayout的一大优点,MotionLayout作为其子类,官方也为它专门提供了强大的可视化编辑器。不过可惜的是,到目前为止还不能使用,下面是MotionEditor的官方预告片的一个节选:

MotionEditor编辑动画
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容

  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,699评论 2 59
  • 紫荆花又落了一地,身边的你又在何方。 昨天下午花花约我去她家,让我帮忙给正在读高一的燕燕讲解数学。 花花是我小时候...
    安晓晓阅读 308评论 0 2
  • 起風了,唯有努力試著生存。 from「海濱墓園」 一整天都在外頭跑,老人餃子會實在不想說。午後糾結了一下還是決定坐...
    阿飛阅读 216评论 0 1
  • 2018年3月3日 星期六 天气:晴 晚上朋友来电话,说一起出去吃饭。我没有推迟,叫上老婆孩子就去了。 我这个朋友...
    呜呜呜呜呜呜呜呜阅读 142评论 0 0
  • 原创:王漫 左宗棠,湖南湘阴人,晚清中兴儒将,精通韬略,足智多谋。其一生对中华民族,甚至对自己,最大贡献就是率军抵...
    武商路漫漫阅读 2,442评论 49 28