ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

ConstraintLayout2.x使用详解

2021-05-06 11:33:53  阅读:580  来源: 互联网

标签:动画 float flow 视图 详解 设置 使用 MotionLayout ConstraintLayout2


ConstraintLayout2.x

一、简介

Constraint Layout 是最受欢迎的 Jetpack 库之一,ConstraintLayout2.x不仅包含 1.x 版本中的所有功能,还在 Android Studio(4.0+) 中集成了可以直接预览 XML 的工具,甚至可以直接在预览界面中对布局进行编辑。

二、使用

Constraintlayout参考地址:

https://developer.android.google.cn/reference/androidx/constraintlayout/classes

在项目的build.gradle引入constraint-layout(因为google已经弃用support库,建议迁移到androidx下):

implementation 'androidx.constraintlayout:constraintlayout:2.0.4'

三、约束详解

本文只介绍2.x新增部分内容,1.x部分参考:ConstraintLayout1.x

3.1、Flow(2.0中添加)

Flow(androidx.constraintlayout.helper.widget.Flow)允许将一组控件水平或垂直放置,类似于链(Chains),一组控件的引用使用constraint_referenced_ids来设置,id中间使用逗号隔开。

例如,实现一组控件水平展开放置,超出屏幕,自动换行展示:

image-20210419172657109
<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/bt_flow_a"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="A" />
    <Button
        android:id="@+id/bt_flow_b"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="B" />
    <Button
        android:id="@+id/bt_flow_c"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="C" />
    <Button
        android:id="@+id/bt_flow_d"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="D" />
    <Button
        android:id="@+id/bt_flow_e"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="E" />
    <androidx.constraintlayout.helper.widget.Flow
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:constraint_referenced_ids="bt_flow_a,bt_flow_b,bt_flow_c,bt_flow_d,bt_flow_e"
        app:flow_horizontalGap="20dp"
        app:flow_verticalGap="20dip"
        app:flow_wrapMode="aligned"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

共有配置如下:

  • flow_horizontalStyle = “spread|spread_inside|packed”

    约束所有水平链,下方效果模式使用了 app:flow_wrapMode="aligned"

    • spread效果:
      image-20210419172657109
    • spread_inside效果:
      image-20210419172657109
    • packed效果:
      image-20210419172657109
  • flow_verticalStyle = “spread|spread_inside|packed”

    同横向flow_horizontalStyle

  • flow_horizontalBias = “float

    设置控件所有链的水平偏移,需要app:flow_horizontalStyle="packed"才生效,取值为【0-1】

  • flow_verticalBias = “float

    同flow_horizontalBias

  • flow_horizontalGap = “dimension

    横向间隔

  • flow_verticalGap = “dimension

    纵向间隔

  • flow_horizontalAlign = “start|end”

    水平方向对齐

  • flow_verticalAlign = "top|bottom|center|baseline

    垂直方向对齐.在app:flow_wrapMode="aligned"模式下好像不生效,其他两种模式生效。

flow_wrapMode属性值有三种:

flow
none

这是flow默认模式,创建一个水平或者垂直链,所有引用的视图以一条链的方式进行布局,如果内容溢出则溢出内容不可见:

image-20210419144405585
chain

当出现溢出时,溢出的内容会自动换行,以新的一条链的方式进行布局:

image-20210419144452256

​ 更多配置:

  • flow_firstHorizontalStyle = “spread|spread_inside|packed”

    约束第一条水平链,当有多条链(多行)时,只约束第一条链(第一行),其他链(其他行)不约束

  • flow_lastHorizontalStyle = “spread|spread_inside|packed”
    约束最后一条水平链,当有多条链(多行)时,只约束最后一条链(最后一行),其他链(其他行)不约束

  • flow_firstVerticalStyle = “spread|spread_inside|packed”
    同flow_firstHorizontalStyle

  • flow_lastVerticalStyle = “spread|spread_inside|packed”

    同flow_lastHorizontalStyle

  • flow_firstHorizontalBias = “float

    约束第一条水平链偏移,当有多条链(多行)时,只约束第一条链(第一行),其他链(其他行)不约束

  • flow_lastHorizontalBias = “float

    约束最后一条水平链偏移,当有多条链(多行)时,只约束最后一条链(最后一行),其他链(其他行)不约束

  • flow_firstVerticalBias = “float

    同flow_firstHorizontalBias

  • flow_lastVerticalBias = “float

    同flow_lastHorizontalBiaschain

  • flow_maxElementsWrap

    设置每条链的最大控件的个数

aligned

chain 类似,但是不以行而是以列的方式进行布局:

image-20210419144630957

​ 更多配置同chain

3.2、ImageFilterButton(2.0中添加)、ImageFilterView(2.0中添加)

ImageFilterButton(androidx.constraintlayout.utils.widget.ImageFilterButton)、ImageFilterView(androidx.constraintlayout.utils.widget.ImageFilterView)两个控件基本相同,同ImageView与ImageButton之间的关系

现拿ImageFilterView做简单使用说明:

​ ImageFilterView可对设置的背景图片进行相关的过滤效果设置。

例如:想给一个方形图片设置圆角

image-20210420094755877
<androidx.constraintlayout.utils.widget.ImageFilterView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="20dip"
    app:round="20dip"
    android:background="@mipmap/ic_launcher"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

相关属性解释:

  • round=“dimension”

    设置图片圆角大小

  • roundPercent=“float”

    设置图片圆角率【0-1】,如果图片是一个正方形,此时roundPercent设置为1,则是显示一个圆。

  • altSrc=“reference”

    通过altSrc设置的资源,允许交叉淡入淡出

  • saturation=“float”

    设置拖的饱和度。0 =灰度,1 =原始,2 =超饱和

  • brightness=“float”

    设置图片的亮度 0 =黑色,1 =原始,2 =两倍的亮度

  • warmth=“float”

    设置图片表观色温。

  • contrast=“float”

    设置图片对比度。1 =不变,0 =灰色,2 =高对比度

  • crossfade=“float”

    改变使用altSrc设置的资源透明度【0-1】,使得底层图片的显示程度

  • overlay=“boolean”

    设置altSrc设置的资源图像是在原始图像上淡入淡出还是与它交叉淡入淡出。默认为true。

3.3、Layer(2.0中添加)

Layer(androidx.constraintlayout.helper.widget.Layer)可以将一些空间设置为一个图层,可对该图层进行背景色、可见性、elevation、padding、补间动画等一些操作

例:将一个Imageview和TextView同时设置一个背景色

image-20210420094755877
<?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">

    <androidx.constraintlayout.helper.widget.Layer
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ff00ff"
        app:constraint_referenced_ids="iv_layer_img,tv_layer_text"
        app:layout_constraintBottom_toBottomOf="@id/tv_layer_text"
        app:layout_constraintLeft_toLeftOf="@id/iv_layer_img"
        app:layout_constraintRight_toRightOf="@id/iv_layer_img"
        app:layout_constraintTop_toTopOf="@id/iv_layer_img" />

    <ImageView
        android:id="@+id/iv_layer_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dip"
        android:src="@mipmap/ic_launcher"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_layer_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="测试"
        app:layout_constraintLeft_toLeftOf="@id/iv_layer_img"
        app:layout_constraintRight_toRightOf="@id/iv_layer_img"
        app:layout_constraintTop_toBottomOf="@id/iv_layer_img" />
</androidx.constraintlayout.widget.ConstraintLayout>

3.4、MockView(2.0中添加)

MockView(androidx.constraintlayout.utils.widget.MockView),可用于对布局进行原型制作,可以绘制标签(默认为视图ID)以及对角线的基本视图,在构建UI时可用作临时模拟视图。

例如:

image-20210420094755877
<?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">
    <androidx.constraintlayout.utils.widget.MockView
        android:id="@+id/mv_first"
        android:layout_width="80dp"
        android:layout_height="80dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.constraintlayout.utils.widget.MockView
        android:id="@+id/mv_second"
        android:layout_width="80dp"
        android:layout_height="80dp"
        app:layout_constraintLeft_toRightOf="@id/mv_first"
        app:layout_constraintTop_toBottomOf="@id/mv_first" />


</androidx.constraintlayout.widget.ConstraintLayout>

参数详解:

  • mock_label=“string”

    设置中间label文字,默认为控件id

  • mock_labelColor=“color”

    设置label文字颜色

  • mock_labelBackgroundColor=“color”

    设置label背景颜色

  • mock_diagonalsColor=“color”

    设置辅助线颜色

  • mock_showDiagonals=“boolean”

    设置是否显示辅助线

  • mock_showLabel=“boolean”

    设置是否显示label

3.5、MotionLayout(2.0中添加)

MotionLayout(androidx.constraintlayout.motion.widget.MotionLayout)是ConstraintLayout的子类,所以它具有ConstraintLayout所有功能,它能够让开发者给自己的控件更快速的添加动画效果。

MotionLayout布局必须要有一个MotionScene文件,需要在MotionLayout标签中使用app:layoutDescription配置,如果你忘记配置,AndroidSudio会提示你配置,按照提示会自动创建MotionScene文件并配置。

例如:

<?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/activity_motion_layout_scene"
    app:showPaths="true">

    <View
        android:id="@+id/view_start_status"
        android:layout_width="50dip"
        android:layout_height="50dip"
        android:background="@color/black" />

</androidx.constraintlayout.motion.widget.MotionLayout>

activity_motion_layout_scene放置在res/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">

    <ConstraintSet android:id="@+id/start">
        <Constraint android:id="@+id/view_start_status" />
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint android:id="@id/view_start_status" />
    </ConstraintSet>

    <Transition
        app:constraintSetEnd="@id/end"
        app:constraintSetStart="@+id/start" />
</MotionScene>

上面代码是按照AndroidStudio提示自动创建的。

app:showPaths="true" true是显示动画运动轨迹,测试使用。

元素描述:

<ConstraintSet>

描述约束集

<Transition>

描述两个状态或约束集之间的过渡

元素属性:

  • id=“reference”

    Transition描述的ID

  • constraintSetStart=“reference”

    使用ConstraintSet描述的用作开始约束或布局文件作为开始约束【例如:@id/start(ConstraintSet描述)或者@layout/start(布局文件)】

  • constraintSetEnd=“reference”

    使用ConstraintSet描述的用作最终约束或布局文件作为最终约束【例如:@id/end(ConstraintSet描述)或者@layout/end(布局文件)】

  • motionInterpolator=“easeInOut|easeIn|easeOut|linear|bounce”

    设置差值器【例如:】

    • linear:线性效果
    • easeIn:缓入效果
    • easeOut:缓出效果
    • easeInOut:缓入缓出效果
    • bounce:弹簧效果
  • duration=“float”

    执行过渡动画所需的时间

  • staggered=“float”

    float类型,交错移动物体的快速方法

  • autoTransition=“none|jumpToStart|jumpToEnd|animateToStart|animateToEnd”

    设置动画自动执行效果,无需用户触发(点击或移动)动画

    • none:默认效果
    • jumpToStart:直接到开始约束效果
    • jumpToEnd:直接到最终约束效果
    • animateToStart:执行过度动画到开始约束的效果
    • animateToEnd:执行过度动画到最终约束的效果
<OnSwipe>

可选参数,增加了对触摸处理的支持,一个<Transition> 标签下可以包含多个<OnSwipe>

  • touchAnchorId=“reference”

    视图的Id,滑动此视图外的区域,能响应滑动效果

  • touchRegionId=“reference”

    视图的Id,滑动此视图内的区域,能响应滑动效果

  • touchAnchorSide=“top|left|right|bottom|middle|start|end”

    用户滑动界面时MotionLayout将尝试在touchAnchorId指定的视图和手指之间保持一个恒定的距离,而此属性指定的是手指和View的哪一侧保持恒定的距离(left、right、top、buttom)

  • maxVelocity=“float”

    目标视图的最大速度,当滑动一定速度,目标视图会按照惯性继续运作,进行先加速后减速运行(默认情况)。如果视图运动过程中加速到了我们设置的最大值,那么就是先加速,然后按最大速度匀速运动,最后再减速运动。

  • dragDirection=“dragUp|dragDown|dragLeft|dragRight|dragStart|dragEnd”

    滑动的方向: dragUp(手指从下往上拖动(↑))、 dragDown(手指从上往下拖动(↓)) 、dragLeft(手指从右往左拖动(←))、 dragRight(手指从左往右拖动(→))

  • maxAcceleration=“float”

    目标视图的最大加速度,如果想让视图运动更快,则加大其值。默认值1.2

  • dragScale=“float”

    控制视图相对于滑动长度的移动距离。默认值1.0(视图移动的距离应与滑动距离一致),当值小于1时,视图移动的距离会远远小于滑动距离(例如dragScale值为0.5 , 如果滑动了2dp,目标视图会移动1dp),当值大于1时,视图移动的距离会大于滑动距离(例如dragScale值为1.5 , 如果滑动了2dp,目标视图会移动3dp)

  • moveWhenScrollAtTop=“boolean”

    boolean类型,如果滑动是滚动的,并且View(例如RecyclerView或NestedScrollView)同时滚动和过渡

<OnClick>

可选参数,增加了对触发处理的支持

元素属性:

  • targetId=“reference”

    设置用来触发过渡动画的那个 View 的 Id

  • clickAction=“toggle|transitionToEnd|transitionToStart|jumpToEnd|jumpToStart”

    点击时视图执行的动作

    • toggle:在 Start场景和 End 场景之间循环的切换。
    • transitionToEnd:过渡到 End 场景
    • transitionToStart:过渡到 Start场景
    • jumpToEnd:不执行过渡动画跳到 End 场景
    • jumpToStart:不执行过渡动画跳到 Start 场景

<KeyFrameSet>

描述一组Key对象,这些对象可以修改约束集之间的动画。

<KeyPosition>

在动画期间控制布局位置

  • motionTarget=“reference”

    修改路径的视图id

  • framePosition=“integer”

    表示动画的进度,取值范围为[0,100]。30就表示动画进度执行30%的地方

  • transitionEasing=“standard|accelerate|decelerate|linear”

    使用的插值器

    standard:标准

    accelerate:加速

    decelerate:减速

    linear:线性的

  • pathMotionArc=“none|startVertical|startHorizontal|flip”

    动画以弧形运行,需要在起始的 ConstraintSet 也要加入pathMotionArc属性,此时关键帧处加入pathMotionArc也能生效

    none:直线运行

    startVertical:纵向弧形

    startHorizontal:横向弧形

    flip:当前弧形翻转

    例如:正常不添加关键帧,只是控制View的约束位置,不设置其他参数,执行效果如下直线效果

    Apr-23-2021 15-43-57

此时,在起始的 ConstraintSet 也要加入pathMotionArc属性效果如下:

pathMotionArc="startHorizontal"
Apr-23-2021 15-43-57
pathMotionArc="startVertical
Apr-23-2021 15-43-57

此时加入一个关键帧的效果

<KeyFrameSet>
    <KeyPosition
        app:motionTarget="@id/view_start_status"
        app:framePosition="50"
        app:keyPositionType="parentRelative"
        app:percentX="0.5"
        app:percentY="0.3" />
</KeyFrameSet>
Apr-23-2021 16-02-29

在关键帧处设置pathMotionArc="startHorizontal"

<KeyFrameSet>
    <KeyPosition
        app:motionTarget="@id/view_start_status"
        app:framePosition="50"
        app:pathMotionArc="startHorizontal"
        app:keyPositionType="parentRelative"
        app:percentX="0.5"
        app:percentY="0.3" />
</KeyFrameSet>
Apr-23-2021 16-07-34

在关键帧处设置pathMotionArc="none"

<KeyFrameSet>
    <KeyPosition
        app:motionTarget="@id/view_start_status"
        app:framePosition="50"
        app:pathMotionArc="none"
        app:keyPositionType="parentRelative"
        app:percentX="0.5"
        app:percentY="0.3" />
</KeyFrameSet>
Apr-23-2021 16-10-15

在关键帧处设置pathMotionArc="flip

<KeyFrameSet>
    <KeyPosition
        app:motionTarget="@id/view_start_status"
        app:framePosition="50"
        app:pathMotionArc="flip"
        app:keyPositionType="parentRelative"
        app:percentX="0.5"
        app:percentY="0.3" />
</KeyFrameSet>
Apr-23-2021 16-15-55
  • keyPositionType=“deltaRelative|pathRelative|parentRelative”

    关键点的依赖坐标系坐标系

    详细说明:

    parentRelative:相对父容器

    <KeyFrameSet>
        <KeyPosition
            app:motionTarget="@id/view_start_status"
            app:framePosition="20"
            app:keyPositionType="parentRelative"
            app:percentX="0.3"
            app:percentY="0.1" />
    </KeyFrameSet>
    

    image-20210423141429495 image-20210423141542015

    左图为相对与MotionLayout布局建立坐标系,可以看出实际的P点与坐标系中坐标相交点略微偏差。那是因为建立坐标系需要依赖视图本身的中心点为依据,如果你的视图足够小,小到为一个点,此时这种坐标系就是左图情况。而实际上视图不可能那样小,实际状况如右图,依赖控件本身的中心点建立坐标系,此时的P点正好为坐标系中的位置。

    deltaRelative:三角定位

    <KeyFrameSet>
        <KeyPosition
            app:motionTarget="@id/view_start_status"
            app:framePosition="20"
            app:keyPositionType="deltaRelative"
            app:percentX="0.3"
            app:percentY="0.1" />
    </KeyFrameSet>
    
    image-20210423142256321

    此方式是以动画起始状态视图中心点为坐标系(0,0)点,动画结束状态为(1,1)建立坐标系,p点为设置的percentX、percentY具体坐标

    pathRelative:相对路径

    <KeyFrameSet>
        <KeyPosition
            app:motionTarget="@id/view_start_status"
            app:framePosition="20"
            app:keyPositionType="pathRelative"
            app:percentX="0.3"
            app:percentY="0.1" />
    </KeyFrameSet>
    
    image-20210423142514223

    此方式为以动画起始状态视图中心点(0,0),动画结束状态视图中心点(1,0)建立x轴,x顺时针90度建立y轴,y轴1.0长度与x轴相同。p点为设置的percentX、percentY具体坐标。

  • percentX=“float”

    相对参考系的横向的比例【0-1】

  • percentY=“float”

    相对参考系的纵向的比例【0-1】

  • sizePercent=“float”

    如果视图更改大小,则这将控制大小的增长方式。(对于固定大小的对象,请使用<KeyAttributes> scaleX / Y)

    例如设置一个约束起始宽度、高度为50dip ,终止宽度、高度为100dip,如果不设置sizePercent比例效果如下:

    <?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">
        <ConstraintSet android:id="@+id/start">
            <Constraint
                android:id="@+id/view_start_status"
                android:layout_width="50dip"
                android:layout_height="50dip"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>
        </ConstraintSet>
        <ConstraintSet android:id="@+id/end">
            <Constraint
                android:id="@id/view_start_status"
                android:layout_width="100dip"
                android:layout_height="100dip"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>
        </ConstraintSet>
        <Transition
            app:constraintSetEnd="@id/end"
            app:constraintSetStart="@+id/start">
            <KeyFrameSet>
                <KeyPosition
                    app:framePosition="50"
                    app:keyPositionType="deltaRelative"
                    app:motionTarget="@+id/view_start_status"
                    app:percentX="0.9"
                    app:percentY="0.5" />
            </KeyFrameSet>
        </Transition>
    </MotionScene>
    
Apr-30-2021 13-15-27

​ 如果设置sizePercent为0.3,效果如下:

<KeyPosition
    app:framePosition="50"
    app:sizePercent="0.3"
    app:keyPositionType="deltaRelative"
    app:motionTarget="@+id/view_start_status"
    app:percentX="0.9"
    app:percentY="0.5" />
Apr-30-2021 13-18-21

​ 如果设置sizePercent为0.9,效果如下:

<KeyPosition
    app:framePosition="50"
    app:sizePercent="0.3"
    app:keyPositionType="deltaRelative"
    app:motionTarget="@+id/view_start_status"
    app:percentX="0.9"
    app:percentY="0.5" />
Apr-30-2021 13-20-20
  • percentWidth=“float”

    宽度变化的百分比,如果宽度没有变化,则此属性无效。将会覆盖sizePercent。

    例如设置一个约束起始宽度为50dip ,终止宽度为100dip,如果不设置percentWidth比例效果如下:

    <?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">
        <ConstraintSet android:id="@+id/start">
            <Constraint
                android:id="@+id/view_start_status"
                android:layout_width="50dip"
                android:layout_height="50dip"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>
        </ConstraintSet>
        <ConstraintSet android:id="@+id/end">
            <Constraint
                android:id="@id/view_start_status"
                android:layout_width="100dip"
                android:layout_height="50dip"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>
        </ConstraintSet>
        <Transition
            app:constraintSetEnd="@id/end"
            app:constraintSetStart="@+id/start">
            <KeyFrameSet>
                <KeyPosition
                    app:framePosition="20"
                    app:keyPositionType="deltaRelative"
                    app:motionTarget="@+id/view_start_status"
                    app:percentX="0.3"
                    app:percentY="0.1" />
            </KeyFrameSet>
        </Transition>
    </MotionScene>
    
Apr-30-2021 10-29-03

​ 如果设置percentWidth为0.3,效果如下:

<KeyPosition
    app:framePosition="20"
    app:percentWidth="0.3"
    app:keyPositionType="deltaRelative"
    app:motionTarget="@+id/view_start_status"
    app:percentX="0.3"
    app:percentY="0.1" />
Apr-30-2021 10-32-07

​ 如果设置percentWidth为0.9,效果如下:

<KeyPosition
    app:framePosition="20"
    app:percentWidth="0.3"
    app:keyPositionType="deltaRelative"
    app:motionTarget="@+id/view_start_status"
    app:percentX="0.9"
    app:percentY="0.1" />
Apr-30-2021 10-36-39
  • percentHeight=“float”

    高度变化的百分比,如果高度没有变化,则此属性无效。将会覆盖sizePercent。具体效果同percentWidth

  • curveFit=“spline|linear”

    设置动画运动路径:spline(曲线,默认)、linear(直线)

    如果不设置curveFit属性或设置curveFit=“spline"(它将是曲线运行):

    <KeyPosition
        app:framePosition="50"
        app:keyPositionType="deltaRelative"
        app:motionTarget="@+id/view_start_status"
        app:percentX="0.9"
        app:percentY="0.5" />
    
Apr-30-2021 10-54-18

​ 如果设置curveFit=“linear”(它将是直线运行)

<KeyPosition
app:framePosition="50"
app:keyPositionType="deltaRelative"
app:curveFit="linear"
app:motionTarget="@+id/view_start_status"
app:percentX="0.9"
app:percentY="0.5" />
Apr-30-2021 10-56-41
  • drawPath=“none|path|pathRelative|deltaRelative|asConfigured|rectangles”

    调试使用,绘制布局动画路径及参考线。

    例如设置drawPath=“pathRelative”

    <KeyPosition
        app:framePosition="50"
        app:sizePercent="0.9"
        app:drawPath="pathRelative"
        app:keyPositionType="deltaRelative"
        app:motionTarget="@+id/view_start_status"
        app:percentX="0.9"
        app:percentY="0.5" />
    
Apr-30-2021 13-28-06

​ 设置drawPath=“rectangles”

<KeyPosition
    app:framePosition="50"
    app:sizePercent="0.9"
    app:drawPath="rectangles"
    app:keyPositionType="deltaRelative"
    app:motionTarget="@+id/view_start_status"
    app:percentX="0.9"
    app:percentY="0.5" />
Apr-30-2021 13-30-30

​ 设置drawPath=“deltaRelative”

<KeyPosition
    app:framePosition="50"
    app:sizePercent="0.9"
    app:drawPath="deltaRelative"
    app:keyPositionType="deltaRelative"
    app:motionTarget="@+id/view_start_status"
    app:percentX="0.9"
    app:percentY="0.5" />
Apr-30-2021 13-32-03
<KeyAttribute>

在动画期间控制布局属性

例如:实现View在移动过程中增加自身的缩放、透明度、旋转等属性的改变

<KeyFrameSet>
    <KeyAttribute
        android:scaleX="0.1"
        android:scaleY="0.1"
        app:framePosition="50"
        app:motionTarget="@id/view_start_status" />
    <KeyAttribute
        android:alpha="0.1"
        app:framePosition="50"
        app:motionTarget="@id/view_start_status" />
    <KeyAttribute
        android:rotation="-45"
        app:framePosition="50"
        app:motionTarget="@id/view_start_status" />
</KeyFrameSet>
Apr-25-2021 10-57-49
  • motionTarget=“reference”

修改属性的视图id

  • framePosition=“integer”

表示动画的进度,取值范围为[0,100]。30就表示动画进度执行30%的地方

常用相关属性参数

  • scaleX=“float”

    视图的宽度变化比例

  • scaleY=“float”

    视图的高度变化比例

  • rotation=“float”

    视图旋转的角度(以度为单位)

  • rotationX

    视图绕x轴旋转的角度(以度为单位)

  • rotationY

    视图绕y轴旋转的角度(以度为单位)

  • alpha=“float”

    视图的透明度变化【0-1】

  • elevation=“dimension”

    视图基于Z轴的高度

  • translationX=“dimension”

    视图在X轴上位移的距离

  • translationY=“dimension”

    视图在Y轴上位移的距离

  • translationZ=“dimension”

    视图在Z轴上位移的距离

<CustomAttribute>

通过反射调用设置的“名称”方法

例如在关键帧处改变背景色

  <KeyAttribute
        app:framePosition="0"
        app:motionTarget="@id/view_start_status">
        <CustomAttribute
            app:attributeName="BackgroundColor"
            app:customColorValue="#00ffff" />
    </KeyAttribute>
    <KeyAttribute
        app:framePosition="50"
        app:motionTarget="@id/view_start_status">
        <CustomAttribute
            app:attributeName="BackgroundColor"
            app:customColorValue="#00ff00" />
    </KeyAttribute>
</KeyFrameSet>
Apr-25-2021 13-27-43
  • attributeName=“string”

    属性的名称。区分大小写。(BackgroundColor将寻找方法setBackgroundColor(…)

  • customColorValue=“color”

    反射的属性为一个颜色值的属性

  • customIntegerValue=“integer”

    反射的属性为一个integer值的属性

  • customFloatValue=“float”

    反射的属性为一个float值的属性

  • customStringValue=“string”

    反射的属性为一个string值的属性

  • customDimension=“dimension”

    反射的属性为一个dimension值的属性

  • customBoolean=“boolean”

    反射的属性为一个boolean(true/false)值的属性

  • <KeyCycle>

    https://github.com/googlearchive/android-ConstraintLayoutExamples/releases/download/1.0/CycleEditor.jar

    控制动画过程中做周期性

    例如,实现View在移动动画使用正弦函数模式进行属性改变

    <KeyFrameSet>
        <KeyCycle
            android:rotation="45"
            app:framePosition="50"
            app:motionTarget="@id/view_start_status"
            app:waveOffset="0"
            app:wavePeriod="1"
            app:waveShape="sin" />
    </KeyFrameSet>
    
    Apr-25-2021 14-00-04
  • motionTarget=“reference”

    修改属性的视图id

  • framePosition=“integer”

    表示动画的进度,取值范围为[0,100]。30就表示动画进度执行30%的地方

  • waveOffset=“float”

    偏移值已添加到属性

  • wavePeriod=“float”

    在该区域附近循环的循环数

  • waveShape=“sin|square|triangle|sawtooth|reverseSawtooth|cos|bounce”

    产生设置的波的形状

    sin:正弦波

    <KeyFrameSet>
        <KeyCycle
            android:rotation="45"
            app:framePosition="50"
            app:motionTarget="@id/view_start_status"
            app:waveOffset="0"
            app:wavePeriod="1"
            app:waveShape="sin" />
    </KeyFrameSet>
    
    Apr-25-2021 14-00-04

    square:方形波

    <KeyFrameSet>
        <KeyCycle
            android:rotation="30"
            app:framePosition="50"
            app:motionTarget="@id/view_start_status"
            app:waveOffset="0"
            app:wavePeriod="1"
            app:waveShape="square" />
    </KeyFrameSet>
    

    triangle:三角波

    <KeyFrameSet>
        <KeyCycle
            android:rotation="30"
            app:framePosition="50"
            app:motionTarget="@id/view_start_status"
            app:waveOffset="0"
            app:wavePeriod="1"
            app:waveShape="square" />
    </KeyFrameSet>
    

    sawtooth:锯齿波

    <KeyFrameSet>
        <KeyCycle
            android:rotation="30"
            app:framePosition="50"
            app:motionTarget="@id/view_start_status"
            app:waveOffset="0"
            app:wavePeriod="1"
            app:waveShape="sawtooth" />
    </KeyFrameSet>
    

reverseSawtooth:反向锯齿波

<KeyFrameSet>
    <KeyCycle
        android:rotation="30"
        app:framePosition="50"
        app:motionTarget="@id/view_start_status"
        app:waveOffset="0"
        app:wavePeriod="1"
        app:waveShape="reverseSawtooth" />
</KeyFrameSet>

cos:余弦波

<KeyFrameSet>
    <KeyCycle
        android:rotation="30"
        app:framePosition="50"
        app:motionTarget="@id/view_start_status"
        app:waveOffset="0"
        app:wavePeriod="1"
        app:waveShape="cos" />
</KeyFrameSet>
Apr-25-2021 15-59-54

bounce:反弹

<KeyFrameSet>
    <KeyCycle
        android:rotation="30"
        app:framePosition="50"
        app:motionTarget="@id/view_start_status"
        app:waveOffset="0"
        app:wavePeriod="1"
        app:waveShape="bounce" />
</KeyFrameSet>
Apr-25-2021 16-01-06

常用相关属性参数、及<CustomAttribute> 参考<KeyAttribute> 即可

<KeyTimeCycle>

控制动画在帧上做周期性

例如:在移动View未移动是也显示周期性动画,当View移动时同时也执行周期性动画

<KeyFrameSet>
    <KeyTimeCycle
        android:rotation="30"
        app:wavePeriod="1"
        app:framePosition="50"
        app:motionTarget="@id/view_start_status"
       />
</KeyFrameSet>
Apr-26-2021 10-34-40

这里的参数解释:

  • rotation=“float”

​ 视图旋转的角度(以度为单位)

  • wavePeriod=“float”

    在该区域附近循环的循环数

  • motionTarget=“reference”

    修改属性的视图id

  • framePosition="integer“

    表示动画的进度,取值范围为[0,100]。50就表示动画进度执行50%的地方

​ 其他参数同<KeyCycle>

<KeyTrigger>

在动画过程中的固定点触发回调到代码中

例如: 在动画执行过程中,监听动画执行进度。

Apr-26-2021 11-08-13

实现步骤:

1、定义KeyTrigger

<KeyFrameSet>
    <KeyTrigger
        app:framePosition="20"
        app:motionTarget="@id/tv_text"
        app:onCross="p0" />
    <KeyTrigger
        app:framePosition="50"
        app:motionTarget="@id/tv_text"
        app:onCross="p1" />
    <KeyTrigger
        app:framePosition="80"
        app:motionTarget="@id/tv_text"
        app:onCross="p2" />
</KeyFrameSet>

参数解释:

  • motionTarget=“reference”

    目标视图id(这里是自定义视图,因为方法写在了自定义视图里)

  • framePosition=“integer”

    表示动画的进度,取值范围为[0,100]。50就表示动画进度执行50%的地方

  • onCross=“string”

    方法名称,于自定义视图中方法名一一对应。不管动画是正向还是反向,只要到达设置的framePosition 就会执行函数

  • onPositiveCross=“string”

    方法名称,于自定义视图中方法名一一对应。只有正向执行动画是到达设置的framePosition 才会执行函数

  • onNegativeCross=“string”

    方法名称,于自定义视图中方法名一一对应。只有反向执行动画是到达设置的framePosition 才会执行函数

    Apr-26-2021 13-56-09
  • triggerSlack=“float”

    如果动画位置未离开framePosition触发点,则不会重复调用触发器(值越大 重复率越低)

  • triggerId=“reference”

    使用此ID回调TransitionListener监听中的onTransitionTrigger中方法

  • motion_postLayoutCollision=“boolean”

    Define motion pre or post layout. Post layout is more expensive but captures KeyAttributes or KeyCycle motions.

  • motion_triggerOnCollision=“reference”

    (id) Trigger if the motionTarget collides with the other motionTarget

2、自定义视图:

class MyText(context: Context, attrs: AttributeSet) : AppCompatTextView(context, attrs) {
    fun p0() {
        text = "20%"
    }
    fun p1() {
        text = "50%"
    }
    fun p2() {
        text = "80%"
    }
}

3、使用自定义视图

<?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:showPaths="true"
    app:layoutDescription="@xml/activity_motion_layout7_scene">

    <View
        android:id="@+id/view_start_status"
        android:layout_width="50dip"
        android:layout_height="50dip"
        android:background="@color/black" />

    <com.example.constraintlayout.MyText
        android:id="@+id/tv_text"
        android:layout_width="100dip"
        android:layout_height="50dip"
        android:textColor="@android:color/white"
        android:layout_marginBottom="200dip"
        android:textSize="20sp"
        android:gravity="center"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:background="@color/black"/>
</androidx.constraintlayout.motion.widget.MotionLayout>

MotionLayout常用API

  • setDebugMode(int debugMode)

    设置是否运动进行时是否显示运动路径,用来调试动画,与MotionLayout xml中app:motionDebug对应,在xml 也可以使用app:showPaths="true"来控制是否显示运动路径。

    代码中设置可选参数如下:

    public static final int DEBUG_SHOW_NONE = 0;//不显示路径及进度
    public static final int DEBUG_SHOW_PROGRESS = 1;//只显示进度
    public static final int DEBUG_SHOW_PATH = 2;//只显示路径
    

    xml设置可选参数如下:

    <enum name="NO_DEBUG" value="0"/>//不显示路径及进度
    <enum name="SHOW_PROGRESS" value="1"/>//只显示进度
    <enum name="SHOW_PATH" value="2"/>//只显示路径
    <enum name="SHOW_ALL" value="3"/>//既显示路径页显示进度
    
  • loadLayoutDescription(int motionScene)

    通过代码加载MotionScene,对应的xml中属性为app:layoutDescription

  • transitionToStart()

    切换到动画start状态,默认有过渡效果,如果不需要过渡效果,可以通过setProgress(0)

  • transitionToEnd()

    切换到动画end状态,默认有过渡效果,如果不需要过渡效果,可以通过setProgress(1)

  • setProgress(float pos)

    设置动画运动进度【0-1】

  • transitionToState(int id)

    切换到动画某个状态,可以是start或end状态,参数id指的是ConstraintSet标签定义的id

  • setTransitionListener(MotionLayout.TransitionListener listener)

    监听MotionLayout动画执行过程

    public interface TransitionListener {
      	//开始动画时回调
        void onTransitionStarted(MotionLayout motionLayout,//当前MotionLayout视图
                                 int startId,//开始状态的ID  如果未知,则为-1
                                 int endId //结束状态的ID  如果未知,则为-1
                                 );
    	//动画改变状态时回调
        void onTransitionChange(MotionLayout motionLayout,//当前MotionLayout视图
                                int startId,//开始状态的ID  如果未知,则为-1
                                int endId,//结束状态的ID  如果未知,则为-1
                                float progress //当前动画进度【0-1】
                                );
    	//完成动画时回调
        void onTransitionCompleted(MotionLayout motionLayout,//当前MotionLayout视图
                                   int currentId //到达状态的ID
                                  );
    	//使用<KeyTrigger>中定义了 triggerId,会回调到这里
        void onTransitionTrigger(MotionLayout motionLayout,//当前MotionLayout视图
                                 int triggerId,//使用triggerId设置的ID
                                 boolean positive,//正向运动(start-->end)经过此处返回true,反向运动(end-->start)经过此处 返回false
                                 float progress //当前动画进度【0-1】
                                );
    }
    

使用AndroidStudio中Motion Editor

Motion Editor(Android Studio 4.0 +) 是一款专门针对 MotionLayout 布局类型所构建的可视化编辑器,通过它可以轻松地创建和预览动画效果。当你在一个包含 MotionLayout 的 XML 文件中选择 Design 或 Split 视图时,AndroidStudio 会自动打开 Motion Editor。你可以使用已在布局编辑器中所熟知的交互方式来编辑布局和 Motion Scene 文件,并可以直接在 Android Studio 预览界面中对动画效果进行预览。

image-20210429132528965
预览面板

预览面板的加入使得在处理动画效果时,能够实现快速编辑并立即获取反馈,当你对动画进行细微调整之后,不用再去重新编译和部署,也能直接预览最终的动画效果。

概览面板

MotionLayout 可以对布局的变化做动画处理,在编辑器中该动画可被指定为 ConstraintSets 中的 Transition 效果,Motion Editor 可以通过 概览面板将这些状态的转变可视化(预览面板),要编辑 ConstraintSet 中的约束,点击 概览面板中相应的选项即可。

image-20210429133355742. image-20210429133457750

图中start、end是两个<ConstraintSet> 它们之间有一个 <Transition> 效果,可以通过选择start、end来修改视图的状态

选择面板

选择 面板会根据概览面板中的状态显示相应的控件信息,它有三种显示模式:

  • 选中 概览面板中 Motion Layout 时的模式

    Motion Editor 支持编辑基本的 MotionLayout,当在概览面板中选中 MotionLayout模式之后,你可以选择相应的组件来查看它的约束是否配置正确。

    image-20210429134926647
  • 选中概览面板中 ConstraintSet 时的模式

    当在概览面板中选中 ConstraintSet 时,选择面板会以列表的形式列出所有组件,组件旁边的选中图标意味着该组件被当前的 ConstraintSet 所约束(下图选中的是start,也可以选end)。

    image-20210429135019758
  • 选中 概览面板中 Transition 时的模式

    当在概览面板中选择 Transition 时,你可以通过动画工具栏来控制动画的播放。当选中某个动画后,点击时间轴上的 ▶按钮,可以预览动画效果。

    image-20210429135123586

    ​ 当在概览面板中选择 Transition 时,你可以通过工具栏中添加关键帧来添加关键帧约束

    image-20210429140453240 image-20210429140544551

属性面板

这里的属性面板同 Layout Editor 类似的属性面板,可以在这里对Constraint 的可视化效果进行预览,对Motion Scene 文件中视图的所有属性效果进行修改和添加。

当选择概述面板的选中 ConstraintSet 时,选择面板会以列表的形式列出所有组件,你选择具体组件,这时属性面板会展示组件基础可选修改选项供修改或添加。

image-20210429143416867

当选择概述面板的选中 Transition 时,此时属性面板展示<Transition>基础可选属性选项供修改或添加,下方的选择面板会以列表的形式列出所有动画,你选择具体动画,这时属性面板会展示动画基础可选属性选项供修改或添加。

image-20210429144136384 image-20210429144221216

四、案例

仿Android系统通知栏动画效果
image-20210429144221216

  • 布局文件:
<?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"
    android:background="#EDEDED"
    app:layoutDescription="@xml/activity_motion_layout8_scene"
    app:showPaths="false">
    <androidx.constraintlayout.utils.widget.ImageFilterView
        android:id="@+id/iv_bg"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/white"
        app:round="8dip" />
    <androidx.constraintlayout.utils.widget.ImageFilterView
        android:id="@+id/iv_01"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scaleType="center"
        android:src="@mipmap/p6"
        app:altSrc="@mipmap/p5" />
    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/iv_00"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scaleType="center"
        android:src="@mipmap/p3" />
    <androidx.appcompat.widget.AppCompatTextView1qazxsw21qazx
        android:id="@+id/tv_01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableTop="@mipmap/t1"
        android:drawablePadding="8dip"
        android:gravity="center"
        android:text="无线网络"
        android:textSize="10sp" />
    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tv_02"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableTop="@mipmap/t2"
        android:drawablePadding="8dip"
        android:gravity="center"
        android:text="蓝牙"
        android:textSize="10sp" />
    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tv_03"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableTop="@mipmap/t3"
        android:drawablePadding="8dip"
        android:gravity="center"
        android:text="勿扰"
        android:textSize="10sp" />
    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tv_04"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableTop="@mipmap/t4"
        android:drawablePadding="8dip"
        android:gravity="center"
        android:text="手电筒"
        android:textSize="10sp" />
    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tv_05"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableTop="@mipmap/t5"
        android:drawablePadding="8dip"
        android:gravity="center"
        android:text="旋转屏幕"
        android:textSize="10sp" />
    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tv_06"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableTop="@mipmap/t6"
        android:drawablePadding="8dip"
        android:gravity="center"
        android:text="省电模式"
        android:textSize="10sp" />
    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/iv_02"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/p1" />
    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/iv_04"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scaleType="center"
        android:src="@mipmap/p7" />
    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/iv_03"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/p2" />
</androidx.constraintlayout.motion.widget.MotionLayout>
  • 动画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">

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/iv_bg"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginLeft="5dip"
            android:layout_marginTop="5dip"
            android:layout_marginRight="5dip"
            app:layout_constraintBottom_toBottomOf="@id/iv_04"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>
        <Constraint
            android:id="@+id/iv_00"
            android:layout_width="match_parent"
            android:layout_height="40dip"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/iv_01" />
        <Constraint
            android:id="@+id/iv_01"
            android:layout_width="match_parent"
            android:layout_height="50dip"
            app:layout_constraintTop_toTopOf="parent">
            <CustomAttribute
                app:attributeName="Crossfade"
                app:customFloatValue="0" />
        </Constraint>
        <Constraint
            android:id="@+id/iv_02"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dip"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toBottomOf="@id/tv_03" />
        <Constraint
            android:id="@+id/iv_03"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="20dip"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/tv_06" />
        <Constraint
            android:id="@+id/iv_04"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:alpha="1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/iv_03" />
        <Constraint
            android:id="@+id/tv_01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@id/tv_02"
            app:layout_constraintTop_toBottomOf="@id/iv_01" />

        <Constraint
            android:id="@+id/tv_02"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toRightOf="@id/tv_01"
            app:layout_constraintRight_toLeftOf="@id/tv_03"
            app:layout_constraintTop_toTopOf="@id/tv_01" />
        <Constraint
            android:id="@+id/tv_03"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toRightOf="@id/tv_02"
            app:layout_constraintRight_toLeftOf="@id/tv_04"
            app:layout_constraintTop_toTopOf="@id/tv_01" />
        <Constraint
            android:id="@+id/tv_04"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toRightOf="@id/tv_03"
            app:layout_constraintRight_toLeftOf="@id/tv_05"
            app:layout_constraintTop_toBottomOf="@id/iv_01" />
        <Constraint
            android:id="@+id/tv_05"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toRightOf="@id/tv_04"
            app:layout_constraintRight_toLeftOf="@id/tv_06"
            app:layout_constraintTop_toTopOf="@id/tv_04" />
        <Constraint
            android:id="@+id/tv_06"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toRightOf="@id/tv_05"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="@id/tv_04" />
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/iv_bg"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginLeft="5dip"
            android:layout_marginTop="5dip"
            android:layout_marginRight="5dip"
            app:layout_constraintBottom_toBottomOf="@id/iv_04"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent">

        </Constraint>
        <Constraint
            android:id="@+id/iv_00"
            android:layout_width="match_parent"
            android:layout_height="40dip"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/iv_01" />
        <Constraint
            android:id="@+id/iv_01"
            android:layout_width="match_parent"
            android:layout_height="50dip"
            app:layout_constraintTop_toTopOf="parent">
            <CustomAttribute
                app:attributeName="Crossfade"
                app:customFloatValue="1" />
        </Constraint>
        <Constraint
            android:id="@+id/iv_02"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dip"
            android:layout_marginTop="20dip"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toBottomOf="@id/tv_04" />
        <Constraint
            android:id="@+id/iv_03"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dip"
            android:layout_marginRight="20dip"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/tv_06" />
        <Constraint
            android:id="@+id/iv_04"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:alpha="0"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/iv_03" />
        <Constraint
            android:id="@+id/tv_01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dip"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@id/tv_02"
            app:layout_constraintTop_toBottomOf="@id/iv_00">

        </Constraint>
        <Constraint
            android:id="@+id/tv_02"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toRightOf="@id/tv_01"
            app:layout_constraintRight_toLeftOf="@id/tv_03"
            app:layout_constraintTop_toTopOf="@id/tv_01" />
        <Constraint
            android:id="@+id/tv_03"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toRightOf="@id/tv_02"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="@id/tv_01" />
        <Constraint
            android:id="@+id/tv_04"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dip"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@id/tv_05"
            app:layout_constraintTop_toBottomOf="@id/tv_01" />
        <Constraint
            android:id="@+id/tv_05"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toRightOf="@id/tv_04"
            app:layout_constraintRight_toLeftOf="@id/tv_06"
            app:layout_constraintTop_toTopOf="@id/tv_04" />
        <Constraint
            android:id="@+id/tv_06"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toRightOf="@id/tv_05"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="@id/tv_04" />
    </ConstraintSet>

    <Transition
        app:constraintSetEnd="@id/end"
        app:constraintSetStart="@+id/start">

        <KeyFrameSet>
            <KeyAttribute
                app:framePosition="0"
                app:motionTarget="@id/tv_01">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/transparent" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="50"
                app:motionTarget="@id/tv_01">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/transparent" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="100"
                app:motionTarget="@id/tv_01">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/black" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="0"
                app:motionTarget="@id/tv_02">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/transparent" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="50"
                app:motionTarget="@id/tv_02">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/transparent" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="100"
                app:motionTarget="@id/tv_02">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/black" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="0"
                app:motionTarget="@id/tv_03">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/transparent" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="50"
                app:motionTarget="@id/tv_03">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/transparent" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="100"
                app:motionTarget="@id/tv_03">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/black" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="0"
                app:motionTarget="@id/tv_04">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/transparent" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="50"
                app:motionTarget="@id/tv_04">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/transparent" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="100"
                app:motionTarget="@id/tv_04">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/black" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="0"
                app:motionTarget="@id/tv_05">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/transparent" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="50"
                app:motionTarget="@id/tv_05">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/transparent" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="100"
                app:motionTarget="@id/tv_05">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/black" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="0"
                app:motionTarget="@id/tv_06">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/transparent" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="50"
                app:motionTarget="@id/tv_06">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/transparent" />
            </KeyAttribute>
            <KeyAttribute
                app:framePosition="100"
                app:motionTarget="@id/tv_06">
                <CustomAttribute
                    app:attributeName="TextColor"
                    app:customColorValue="@android:color/black" />
            </KeyAttribute>
            <KeyAttribute
                android:alpha="0"
                app:framePosition="0"
                app:motionTarget="@id/iv_00" />
            <KeyAttribute
                android:alpha="0"
                app:framePosition="70"
                app:motionTarget="@id/iv_00" />
            <KeyAttribute
                android:alpha="1"
                app:framePosition="100"
                app:motionTarget="@id/iv_00" />
            <KeyAttribute
                android:alpha="0"
                app:framePosition="0"
                app:motionTarget="@id/iv_02" />
            <KeyAttribute
                android:alpha="0"
                app:framePosition="50"
                app:motionTarget="@id/iv_02" />
            <KeyAttribute
                android:alpha="1"
                app:framePosition="100"
                app:motionTarget="@id/iv_02" />
            <KeyAttribute
                android:alpha="0"
                app:framePosition="0"
                app:motionTarget="@id/iv_03" />
            <KeyAttribute
                android:alpha="0"
                app:framePosition="50"
                app:motionTarget="@id/iv_03" />
            <KeyAttribute
                android:alpha="1"
                app:framePosition="100"
                app:motionTarget="@id/iv_03" />

            <KeyPosition
                app:framePosition="50"
                app:keyPositionType="pathRelative"
                app:motionTarget="@id/tv_01"
                app:percentX="0.4"
                app:percentY="0" />
            <KeyPosition
                app:framePosition="50"
                app:keyPositionType="pathRelative"
                app:motionTarget="@id/tv_02"
                app:percentX="0.4"
                app:percentY="0" />
            <KeyPosition
                app:framePosition="50"
                app:keyPositionType="pathRelative"
                app:motionTarget="@id/tv_03"
                app:percentX="0.4"
                app:percentY="0" />
            <KeyPosition
                app:framePosition="10"
                app:keyPositionType="deltaRelative"
                app:motionTarget="@id/tv_04"
                app:percentX="0.1"
                app:percentY="0.3" />
            <KeyPosition
                app:framePosition="10"
                app:keyPositionType="deltaRelative"
                app:motionTarget="@id/tv_05"
                app:percentX="0.1"
                app:percentY="0.3" />
            <KeyPosition
                app:framePosition="10"
                app:keyPositionType="deltaRelative"
                app:motionTarget="@id/tv_06"
                app:percentX="0.1"
                app:percentY="0.3" />
            <KeyCycle
                android:rotation="360"
                app:framePosition="0"
                app:motionTarget="@id/iv_03"
                app:waveOffset="0"
                app:wavePeriod="1"
                app:waveShape="sin" />
        </KeyFrameSet>
        <OnSwipe app:dragDirection="dragDown" />
    </Transition>
</MotionScene>

标签:动画,float,flow,视图,详解,设置,使用,MotionLayout,ConstraintLayout2
来源: https://blog.csdn.net/zping0808/article/details/116305678

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有