当前位置: 首页>移动开发>正文

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow

ConstraintLayout2.x

一、简介

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

二、使用

在项目的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中间使用逗号隔开。

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


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_android,第1张

<?xml versinotallow="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效果:
  • spread_inside效果:
  • packed效果:
  • 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属性值有三种:


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_xml_02,第2张

none

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


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_03,第3张

chain

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


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_04,第4张

更多配置:

  • 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 类似,但是不以行而是以列的方式进行布局:


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_05,第5张

更多配置同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可对设置的背景图片进行相关的过滤效果设置。

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


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_android_06,第6张

<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同时设置一个背景色


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_07,第7张

<?xml versinotallow="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时可用作临时模拟视图。

例如:


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_android_08,第8张

<?xml versinotallow="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的约束位置,不设置其他参数,执行效果如下直线效果

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

pathMotionArc="startHorizontal"

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_android_09,第9张

pathMotinotallow="startVertical

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_10,第10张

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

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


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_xml_11,第11张

在关键帧处设置pathMotinotallow="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>


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_12,第12张

在关键帧处设置pathMotinotallow="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>


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_xml_13,第13张

在关键帧处设置pathMotinotallow="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>


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_android_14,第14张

  • 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>

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_xml_15,第15张

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_xml_16,第16张

左图为相对与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>

此方式是以动画起始状态视图中心点为坐标系(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>

此方式为以动画起始状态视图中心点(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>

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_xml_17,第17张

如果设置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" />


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_xml_18,第18张

如果设置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" />


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_19,第19张

  • percentWidth=“float”
    宽度变化的百分比,如果宽度没有变化,则此属性无效。将会覆盖sizePercent。
    例如设置一个约束起始宽度为50dip ,终止宽度为100dip,如果不设置percentWidth比例效果如下:
<?xml versinotallow="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>

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_20,第20张

如果设置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" />


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_xml_21,第21张

如果设置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" />


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_android_22,第22张

  • 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" />

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_android_23,第23张

如果设置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" />


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_24,第24张

  • 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" />

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_25,第25张

设置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" />


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_xml_26,第26张

设置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" />


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_xml_27,第27张

<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>


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_android_28,第28张

  • motinotallow=“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>


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_xml_29,第29张

  • 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>
  • 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>

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>

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_xml_30,第30张

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>


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_xml_31,第31张

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>


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_32,第32张

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>


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_33,第33张

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

<KeyTimeCycle>

控制动画在帧上做周期性

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

<KeyFrameSet>
    <KeyTimeCycle
        android:rotation="30"
        app:wavePeriod="1"
        app:framePosition="50"
        app:motionTarget="@id/view_start_status"
       />
</KeyFrameSet>


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_34,第34张

这里的参数解释:

  • rotation=“float”

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

  • wavePeriod=“float”
    在该区域附近循环的循环数
  • motionTarget=“reference”
    修改属性的视图id
  • framePosition="integer“
    表示动画的进度,取值范围为[0,100]。50就表示动画进度执行50%的地方

其他参数同<KeyCycle>

<KeyTrigger>

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

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


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_xml_35,第35张

实现步骤:

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 才会执行函数
  • 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 预览界面中对动画效果进行预览。


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_android_36,第36张

预览面板

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

概览面板

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

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_37,第37张

.

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_38,第38张

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

选择面板

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

  • 选中 概览面板中 Motion Layout 时的模式
    Motion Editor 支持编辑基本的 MotionLayout,当在概览面板中选中 MotionLayout模式之后,你可以选择相应的组件来查看它的约束是否配置正确。
  • 选中概览面板中 ConstraintSet 时的模式
    当在概览面板中选中 ConstraintSet 时,选择面板会以列表的形式列出所有组件,组件旁边的选中图标意味着该组件被当前的 ConstraintSet 所约束(下图选中的是start,也可以选end)。
  • 选中 概览面板中 Transition 时的模式
    当在概览面板中选择 Transition 时,你可以通过动画工具栏来控制动画的播放。当选中某个动画后,点击时间轴上的 ▶按钮,可以预览动画效果。

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

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_android_39,第39张

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_android_40,第40张

属性面板

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

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


ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_41,第41张

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

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_42,第42张

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_android_43,第43张

四、案例

仿Android系统通知栏动画效果

ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow,ConstraintLayout中间嵌套一个NestedScrollView constraintlayout flow_控件_44,第44张

  • 布局文件:
<?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>


https://www.xamrdz.com/mobile/4xm1930643.html

相关文章: