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中间使用逗号隔开。
例如,实现一组控件水平展开放置,超出屏幕,自动换行展示:
<?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效果:
- spread_inside效果:
- packed效果:
- spread效果:
-
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
属性值有三种:
none
这是flow默认模式,创建一个水平或者垂直链,所有引用的视图以一条链的方式进行布局,如果内容溢出则溢出内容不可见:
chain
当出现溢出时,溢出的内容会自动换行,以新的一条链的方式进行布局:
更多配置:
-
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
类似,但是不以行而是以列的方式进行布局:
更多配置同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可对设置的背景图片进行相关的过滤效果设置。
例如:想给一个方形图片设置圆角
<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同时设置一个背景色
<?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时可用作临时模拟视图。
例如:
<?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的约束位置,不设置其他参数,执行效果如下直线效果
此时,在起始的 ConstraintSet 也要加入pathMotionArc属性效果如下:
pathMotionArc="startHorizontal"
pathMotionArc="startVertical
此时加入一个关键帧的效果
<KeyFrameSet>
<KeyPosition
app:motionTarget="@id/view_start_status"
app:framePosition="50"
app:keyPositionType="parentRelative"
app:percentX="0.5"
app:percentY="0.3" />
</KeyFrameSet>
在关键帧处设置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>
在关键帧处设置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>
在关键帧处设置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>
-
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>
左图为相对与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>
如果设置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" />
如果设置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" />
-
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>
如果设置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" />
如果设置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" />
-
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" />
如果设置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" />
-
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" />
设置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" />
设置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" />
<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>
- 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>
-
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>
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>
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>
常用相关属性参数、及<CustomAttribute>
参考<KeyAttribute>
即可
<KeyTimeCycle>
控制动画在帧上做周期性
例如:在移动View未移动是也显示周期性动画,当View移动时同时也执行周期性动画
<KeyFrameSet>
<KeyTimeCycle
android:rotation="30"
app:wavePeriod="1"
app:framePosition="50"
app:motionTarget="@id/view_start_status"
/>
</KeyFrameSet>
这里的参数解释:
- rotation=“float”
视图旋转的角度(以度为单位)
-
wavePeriod=“float”
在该区域附近循环的循环数
-
motionTarget=“reference”
修改属性的视图id
-
framePosition="integer“
表示动画的进度,取值范围为[0,100]。50就表示动画进度执行50%的地方
其他参数同<KeyCycle>
<KeyTrigger>
在动画过程中的固定点触发回调到代码中
例如: 在动画执行过程中,监听动画执行进度。
实现步骤:
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 预览界面中对动画效果进行预览。
预览面板
预览面板的加入使得在处理动画效果时,能够实现快速编辑并立即获取反馈,当你对动画进行细微调整之后,不用再去重新编译和部署,也能直接预览最终的动画效果。
概览面板
MotionLayout 可以对布局的变化做动画处理,在编辑器中该动画可被指定为 ConstraintSets 中的 Transition 效果,Motion Editor 可以通过 概览面板将这些状态的转变可视化(预览面板),要编辑 ConstraintSet 中的约束,点击 概览面板中相应的选项即可。
.
图中start、end是两个<ConstraintSet>
它们之间有一个 <Transition>
效果,可以通过选择start、end来修改视图的状态
选择面板
选择 面板会根据概览面板中的状态显示相应的控件信息,它有三种显示模式:
-
选中 概览面板中 Motion Layout 时的模式
Motion Editor 支持编辑基本的 MotionLayout,当在概览面板中选中 MotionLayout模式之后,你可以选择相应的组件来查看它的约束是否配置正确。
-
选中概览面板中 ConstraintSet 时的模式
当在概览面板中选中 ConstraintSet 时,选择面板会以列表的形式列出所有组件,组件旁边的选中图标意味着该组件被当前的 ConstraintSet 所约束(下图选中的是start,也可以选end)。
-
选中 概览面板中 Transition 时的模式
当在概览面板中选择 Transition 时,你可以通过动画工具栏来控制动画的播放。当选中某个动画后,点击时间轴上的 ▶按钮,可以预览动画效果。
当在概览面板中选择 Transition 时,你可以通过工具栏中添加关键帧来添加关键帧约束
属性面板
这里的属性面板同 Layout Editor 类似的属性面板,可以在这里对Constraint 的可视化效果进行预览,对Motion Scene 文件中视图的所有属性效果进行修改和添加。
当选择概述面板的选中 ConstraintSet 时,选择面板会以列表的形式列出所有组件,你选择具体组件,这时属性面板会展示组件基础可选修改选项供修改或添加。
当选择概述面板的选中 Transition 时,此时属性面板展示<Transition>
基础可选属性选项供修改或添加,下方的选择面板会以列表的形式列出所有动画,你选择具体动画,这时属性面板会展示动画基础可选属性选项供修改或添加。
四、案例
仿Android系统通知栏动画效果
- 布局文件:
<?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>