MotionLayout系列文章:
Android | MotionLayout入门级使用教程(一):https://blog.csdn.net/u013700502/article/details/138142228
关键帧KeyFrameSet
在 MotionScene 中,KeyFrameSet 是一个用于定义动画关键帧的容器,它可以在动画的特定时间点上对属性进行插值或触发事件,从而控制动画的行为和效果。通过关键帧,可以定义精确的动画轨迹和行为,而不是仅仅依赖 MotionLayout 的默认插值。
KeyFrameSet 包含多个关键帧类型,每种类型可以控制不同的属性或触发不同的事件。这些关键帧通过动画的时间轴 (framePosition) 定位。
KeyPosition
KeyPosition 是一个关键帧类型,用于定义视图在动画路径上的位置和大小变化。它允许通过百分比指定位置、大小、角度等属性,以便在特定的动画帧上对元素进行更精细的控制。以下是常用属性及其含义:
- framePosition:定义动画帧的位置,表示在动画中的百分比位置。范围为 0-100,例如 framePosition=“50” 表示动画的中点。
- motionTarget:指定该关键帧所应用的目标视图,通过 ID 进行引用。
- percentX / percentY:是指在 framePosition 按多大百分比来修改路径(值介于 0.0 到 1.0 之间,允许使用负数值和大于 1 的值)。可以简单理解为:在framePosition,修改motionTarget 的路径,即根据 keyPositionType 确定的坐标移动percentX 或percentY 。
- keyPositionType:定义关键帧位置的计算方式,keyPositionType 属性用于确定如何根据 percentX 或 percentY 来修改路径。它可以是 parentRelative、pathRelative 或 deltaRelative。
parentRelative
: 相对于父容器的位置来计算关键帧,左上角和右下角分别为(0,0)和(1,1)。
deltaRelative
: 基于相对于起始点和结束点的相对偏移量来计算路径,起始坐标和终点坐标分别为(0,0)和(1,1)。
pathRelative
: 基于运动路径的百分比位置,从起点连接到终点的方向是X轴,Y轴的正方向是X轴顺时针转90度的方向。
- percentWidth:表示在宽度上的百分比变化量。若宽度无变化,设置此属性无效,可用来实现逐帧增宽的效果。
- percentHeight:表示在高度上的百分比变化量。同 percentWidth,若高度无变化则无效。
- sizePercent:定义元素的整体大小百分比变化(宽度和高度比例一致),是 percentWidth 和 percentHeight 的简化形式。
- transitionEasing:用于定义在关键帧上应用的缓动效果。可以选择 easeIn, easeOut 等内置类型或自定义曲线。
- pathMotionArc:定义路径的弧度,如 startVertical, startHorizontal 等。用于创建更加平滑的曲线运动。
- curveFit:用于曲线拟合,指定如何连接关键帧。通常用于处理更复杂的曲线路径,取值有:spline样条插值、linear线性插值。
视频介绍:https://www.youtube.com/watch?v=3HQMCyAkWTE
KeyAttribute
KeyAttribute 是在特定时间点上动态修改 UI 元素属性的关键帧类型。通过KeyAttribute,可以在动画的某些时刻(关键帧)对元素的多个属性进行变换,例如透明度、旋转、缩放、平移等,从而实现复杂的动画效果。以下是 KeyAttribute 内各字段的含义:
属性详解
1、framePosition:定义了关键帧在动画进度中的位置,以百分比(0 到 100)表示。例如,framePosition=50 表示动画进行到一半时触发该关键帧,framePosition=“25"表示该关键帧会在动画的 25% 处生效。
2、motionTarget:指定关键帧的目标视图或组件,通常使用视图 ID 引用。如:motionTarget=”@+id/my_view" 用于指定应用此关键帧的视图。
3、transitionEasing:控制该关键帧的动画缓动效果,例如线性或非线性变化。示例:transitionEasing=“easeInOut” 表示从平滑启动到渐渐加速,最后减速结束。
4、curveFit:定义了属性在动画中的插值方式,如线性或样条插值。示例:curveFit=“spline” 表示使用样条插值。
5、motionProgress:用于控制 MotionLayout 内的嵌套动画进度,范围从 0 到 1。示例:在某个关键帧时,可以通过 motionProgress=“0.5” 将进度条设为一半。
除上面的属性之外,还可以设置一些View相关的属性,从而对视图的外观和变换进行调整。
android:alpha:设置透明度。android:alpha=“0.5” 表示视图半透明。
android:elevation:控制视图的高度,以增加视图的层次感。示例:android:elevation=“10dp” 表示将视图抬升 10dp。
android:rotation、android:rotationX、android:rotationY:分别控制视图的二维和三维旋转角度。示例:android:rotation=“45” 将视图顺时针旋转 45 度。
android:transformPivotX、android:transformPivotY:控制旋转和缩放的中心点。示例:android:transformPivotX=“50dp” 表示将 X 轴的旋转中心设置在 50dp 处。
transformPivotTarget:指定其他视图作为旋转和缩放的中心点。
transitionPathRotate:视图沿动画路径旋转的角度。示例:transitionPathRotate=“30” 将视图沿路径旋转 30 度。
android:scaleX、android:scaleY:分别设置视图的 X 和 Y 轴缩放。示例:android:scaleX=“1.5” 将视图宽度放大 1.5 倍。
android:translationX、android:translationY:控制视图的 X、Y 方向偏移。示例:android:translationX=“20dp” 表示将视图向右平移 20dp。
android:translationZ:控制视图在 Z 轴上的偏移。
KeyAttribute 提供了灵活的方式去设置动画中的多个关键帧,从而实现复杂且流畅的动画效果。视频介绍:https://www.youtube.com/watch?v=jUm_AkH_mAQ
KeyCycle
KeyCycle在动画过程中做周期性波形来实现动画效果。通过 KeyCycle,可以让属性值以波动的方式进行变化(例如像正弦波或方波),用于产生类似于脉冲或振荡的效果。
属性解释
1、motionTarget:指定应用波动效果的目标视图,通常使用视图的 ID。
2、curveFit:插值方式,决定波形曲线的平滑程度。示例:curveFit=“spline” 表示使用样条插值。
3、framePosition:定义关键帧在动画进度中的位置,以 0 到 100 的百分比表示。示例:framePosition=“50” 表示在动画进度的一半时触发此波动效果。
4、transitionEasing:缓动函数,控制波动动画的速度和流畅度。示例:transitionEasing=“easeInOut”。
5、motionProgress:控制 MotionLayout 内嵌套动画的进度,值从 0 到 1。
6、waveShape:定义波形的形状,可选值包括:
• sin:正弦波
• square:方波
• triangle:三角波
• sawtooth:锯齿波
• reverseSawtooth:反向锯齿波
• cos:余弦
• bounce:弹跳
7、wavePhase:波的初始相位,单位为度数。它控制波形的起始位置。示例:wavePhase=“90” 表示波形从 90 度开始。
8、wavePeriod:波形周期,表示波形重复的频率。值越小,频率越高。示例:wavePeriod=“2” 表示波形每 2 秒完成一个周期。
9、waveOffset:波的偏移量,控制波动效果的初始位置。示例:waveOffset=“0、2” 表示从 0、2 的位置开始波动。
10、waveVariesBy:定义波动变化的方向,可以是 x, y 或 path。示例:waveVariesBy=“x”。
11、 transitionPathRotate:视图沿路径旋转的角度,主要在路径动画中使用。示例:transitionPathRotate=“45” 使视图沿路径旋转 45 度。
其他视图属性,这些属性允许在 KeyCycle 中对视图的多个变换进行波动效果的设置,例如透明度、旋转、缩放和位移等。
• android:alpha:透明度变化。
• android:elevation:视图的高度。
• android:rotation、android:rotationX、android:rotationY:控制二维和三维旋转。
• android:scaleX、android:scaleY:控制 X 和 Y 轴缩放。
• android:translationX、android:translationY:控制 X 和 Y 方向位移。
• android:translationZ:控制 Z 轴方向位移。
视频介绍:https://www.youtube.com/watch?v=qWmU6emSQ5k
KeyTimeCycle
KeyTimeCycle 用于实现基于时间的周期性动画。通过该关键帧,可以对视图的属性(如透明度、旋转、位移等)产生随时间波动的效果,常用于制作振荡或周期性的动画。
属性详解
- framePosition:动画进度中的位置,以百分比表示(0-100)。确定波动效果的触发时间点。
- motionTarget:指定动画目标视图,通过视图 ID 关联。
- transitionEasing:控制缓动效果,定义波形的流畅性。
- curveFit:曲线拟合方法,用于控制插值的平滑度和效果(如 spline)。
- waveShape:波形的形状,定义周期变化的方式。常用的值包括:
• sin:正弦波
• square:方波
• triangle:三角波
• sawtooth:锯齿波
• reverseSawtooth:反向锯齿波
• cos:余弦
• bounce:弹跳
- wavePeriod:波形周期,控制波形的频率,值越大表示频率越高。
- motionProgress:控制嵌套动画的进度,范围为 0 到 1。
- waveOffset:波的偏移量,用于设置初始波动位置。
- wavePhase:波的相位,定义波的起始角度,以度数表示。
- waveDecay:波的衰减时间,单位为毫秒。控制波动效果随时间逐渐减弱,直到小于 1/256。
除上述属性之外,还可以对视图属性添加波动效果,包括透明度、缩放、旋转和位移等:
• android:alpha:透明度变化。
• android:elevation:视图的高度。
• android:rotation、android:rotationX、android:rotationY:控制二维和三维旋转。
• android:scaleX、android:scaleY:控制 X 和 Y 轴缩放。
• android:translationX、android:translationY:控制 X 和 Y 方向位移。
• android:translationZ:控制 Z 轴方向位移。
视频介绍:https://www.youtube.com/watch?v=us0sOEq_fck
KeyTrigger
KeyTrigger 用于在动画到达特定位置时触发事件函数。通过设置触发条件,响应动画进度的某一特定时刻执行特定操作。
- framePosition:触发事件的位置,以动画进度的百分比表示。范围是 0 到 100。
- motionTarget:指定触发事件的目标视图,通过视图 ID 进行关联。
- triggerReceiver:指定接收触发事件的视图,可以是另一个视图的 ID。
- onNegativeCross、onPositiveCross、onCross:这些属性指定了事件的回调函数,分别在以下条件下调用:
• onNegativeCross:动画经过 framePosition 向负方向(回退)触发。
• onPositiveCross:动画经过 framePosition 向正方向(前进)触发。
• onCross:动画经过 framePosition 时(无论方向)触发。- viewTransitionOnNegativeCross、viewTransitionOnPositiveCross、viewTransitionOnCross:这些属性指定了动画过度操作的 ID,对应 onNegativeCross、onPositiveCross 和 onCross 的触发条件。
- triggerSlack:触发的容差,表示触发事件前后的范围,用于避免重复触发。
- triggerId:触发事件的唯一 ID,用于在回调中识别触发事件。
- motion_postLayoutCollision:布尔值,设置为 true 时将在布局后检测碰撞触发。
- motion_triggerOnCollision:指定目标视图的 ID,用于检测 motionTarget 和目标视图的碰撞,发生碰撞时触发事件。
使用示例:
class TriggerText @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : TextView(context, attrs, defStyleAttr) {
fun forwardText() {
text = "我要前进"
}
fun retreatText() {
text = "我要后退"
}
}
自定义了TextView并声明了两个方法用以改变文本,XML布局文件如下:
<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/fragment_cl_img_filter_scene"
app:showPaths="true">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/iv_filter_bottom"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginBottom="30dp"
android:scaleType="centerCrop"
android:src="@drawable/icon_flower"
app:altSrc="@drawable/icon_cat_h"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<org.ninetripods.mq.study.widget.TriggerText
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/red"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="@+id/iv_filter_bottom"
app:layout_constraintStart_toStartOf="@+id/iv_filter_bottom"
app:layout_constraintTop_toBottomOf="@+id/iv_filter_bottom" />
</androidx.constraintlayout.motion.widget.MotionLayout>
MotionScene 动画核心代码:
<Transition
motion:constraintSetEnd="@id/end"
motion:constraintSetStart="@+id/start"
motion:duration="1000"
motion:motionInterpolator="linear">
<KeyFrameSet>
<!--在动画特定位置执行事件函数-->
<KeyTrigger
motion:framePosition="10"
motion:motionTarget="@+id/tv_content"
motion:onPositiveCross="forwardText"
motion:viewTransitionOnCross="@color/red"
motion:triggerSlack="10"/>
<KeyTrigger
motion:framePosition="90"
motion:motionTarget="@+id/tv_content"
motion:onNegativeCross="retreatText" />
</KeyFrameSet>
<OnSwipe
motion:dragDirection="dragRight"
motion:touchAnchorId="@+id/iv_filter_bottom"
motion:touchAnchorSide="right" />
</Transition>
执行效果:
可以看到第一个 KeyTrigger 在动画进行到 10% 时(前进时)触发 forwardText 方法;第二个 KeyTrigger 在动画进行到 90% 时(后退时)触发 retreatText 方法。
KeyTrigger 可用于动态响应动画进度,或当视图达到某个位置时执行特定逻辑,比如播放音效、显示提示或修改视图属性等。相关视频:https://www.youtube.com/watch?v=Rqfufnlq_KU
示例
官方的一个简单综合示例:
XML代码(layout_motion_moon):
<?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="@drawable/background"
app:layoutDescription="@xml/layout_moon_scene"
app:showPaths="true">
<ImageView
android:id="@+id/moon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_moon" />
<TextView
android:id="@+id/credits"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Virginia P @ Google"
android:textSize="24sp" />
<TextView
android:id="@+id/swipe_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Swipe towards the right" />
</androidx.constraintlayout.motion.widget.MotionLayout>
对应的MotionScene代码:
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start"
motion:duration="3000">
<OnSwipe
motion:dragDirection="dragRight"
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom" />
<KeyFrameSet>
<KeyPosition
motion:framePosition="25"
motion:keyPositionType="parentRelative"
motion:motionTarget="@id/moon"
motion:percentY="0.6" />
<KeyPosition
motion:framePosition="50"
motion:keyPositionType="parentRelative"
motion:motionTarget="@id/moon"
motion:percentY="0.5" />
<KeyPosition
motion:framePosition="75"
motion:keyPositionType="parentRelative"
motion:motionTarget="@id/moon"
motion:percentY="0.6" />
<KeyAttribute
android:rotation="-360"
android:scaleX="2.0"
android:scaleY="2.0"
motion:framePosition="50"
motion:motionTarget="@id/moon" />
<KeyAttribute
android:rotation="-720"
android:scaleX="1.0"
android:scaleY="1.0"
motion:framePosition="100"
motion:motionTarget="@id/moon" />
<KeyAttribute
motion:framePosition="0"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFFFFF" />
</KeyAttribute>
<KeyAttribute
motion:framePosition="50"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="@color/yellow" />
</KeyAttribute>
<KeyAttribute
motion:framePosition="100"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFFFFF" />
</KeyAttribute>
<KeyAttribute
android:alpha="0.0"
motion:framePosition="85"
motion:motionTarget="@id/credits" />
<KeyAttribute
android:alpha="0"
motion:framePosition="40"
motion:motionTarget="@id/swipe_hint"
motion:transitionEasing="accelerate" />
</KeyFrameSet>
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/moon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="100dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent" />
<Constraint
android:id="@id/credits"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:alpha="0.0"
motion:layout_constraintBottom_toBottomOf="@id/moon"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="@id/moon" />
<Constraint
android:id="@id/swipe_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:visibility="visible"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/moon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="100dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent" />
<Constraint
android:id="@id/credits"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:alpha="1.0"
motion:layout_constraintBottom_toBottomOf="@id/moon"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="@id/moon" />
<Constraint
android:id="@id/swipe_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
</MotionScene>
大致解释下上述代码的功能:
- ConstraintSet 定义了 start 和 end 状态,描述了动画开始和结束时视图的位置与属性。moon 视图从父布局左下移动到右下,同时改变大小、旋转、颜色等;
- 定义了 OnSwipe 手势控制,允许用户通过向右滑动 moon 的底部触发动画。
- 使用 KeyFrameSet 描述了动画过程中多个关键帧的效果,包括:
位置控制 (KeyPosition):通过 Y 轴的百分比,创建了平滑的移动路径。
属性变化 (KeyAttribute):定义旋转角度、缩放比例、透明度等属性。
自定义属性 (CustomAttribute):改变视图的颜色滤镜。
资料
1、视频介绍:https://www.youtube.com/results?search_query=Motion+Tags
2、CodeLabs:https://codelabs.developers.google.com/codelabs/motion-layout?hl=zh-cn#0