在这里先简单的介绍一下keyFrameSet的使用,最终实现效果如下

<KeyFrameSet>
<KeyPosition
app:framePosition="50"
app:motionTarget="@id/moon"
app:keyPositionType="parentRelative"
app:percentY="0.5"
/>
</KeyFrameSet>
这里的KeyFrameSet是transition的子元素,它是所有关键帧的集合,它包含了KeyPosition,KeyAttribute,KeyCycle,KeyTimeCycle。这里用到的是KeyPosition,它的含义是在framePosition通过percentX或者percentY修改motionTarget的路径,而这里的percentX或者percentY又是根据KeyPositionType来定的。
- framePosition:它的值是0-100,表示动画完成百分之几时的位置
- motionTarget:这个framePosition修改路径的view,也就是修改哪个移动的view的路径
- KeyPositionType:这个framePosition是如何修改这个路径的,它有三个值分别为parentRelative、pathRelative、deltaRelative,至于这三个值的区别接下来会介绍
- percentY|percentX:到framePosition时路径修改为多少,这个值是0.0-1.0,当然负数和大于1的值也可以
实现上述动画的xml为
<MotionScene xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- A transition describes an animation via start and end state -->
<Transition
app:constraintSetStart="@+id/start"
app:constraintSetEnd="@+id/end"
app:duration="3000">
<OnClick
app:targetId="@id/moon"
app:clickAction="toggle"
/>
<KeyFrameSet>
<KeyPosition
app:framePosition="50"
app:motionTarget="@id/moon"
app:keyPositionType="parentRelative"
app:percentY="0.5"
/>
</KeyFrameSet>
</Transition>
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@id/moon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
<Constraint
android:id="@id/credits"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
android:layout_marginStart="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="@id/moon"
app:layout_constraintTop_toTopOf="@id/moon"
/>
</ConstraintSet>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@id/moon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
<Constraint
android:id="@id/credits"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1.0"
android:layout_marginStart="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="@id/moon"
app:layout_constraintTop_toTopOf="@id/moon"
/>
</ConstraintSet>
</MotionScene>
注意要是想显示动画路径需要在activity的xml中加入motionDebug=“SHOW_PATH”,所以activity中的代码为
<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:motionDebug="SHOW_PATH"
app:layoutDescription="@xml/step3_completed" >
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/background"
android:scaleType="centerCrop"
/>
<ImageView
android:id="@+id/moon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_moon"
android:tint="?colorOnPrimary"
/>
<TextView
android:id="@+id/credits"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/credits"
android:textSize="24sp"
android:textColor="?colorOnPrimary" />
</androidx.constraintlayout.motion.widget.MotionLayout>
parentRelative它的坐标系就是屏幕的坐标系



插入多个关键帧,比如实现下面的path

<KeyFrameSet>
<KeyPosition
app:framePosition="25"
app:motionTarget="@id/moon"
app:keyPositionType="parentRelative"
app:percentY="0.6"
/>
<KeyPosition
app:framePosition="50"
app:motionTarget="@id/moon"
app:keyPositionType="parentRelative"
app:percentY="0.5"
/>
<KeyPosition
app:framePosition="75"
app:motionTarget="@id/moon"
app:keyPositionType="parentRelative"
app:percentY="0.6"
/>
</KeyFrameSet>
如果实现下面的path

<KeyFrameSet>
<KeyPosition
app:framePosition="25"
app:motionTarget="@id/moon"
app:keyPositionType="parentRelative"
app:percentY="0.6"
app:percentX="0.1"
/>
<KeyPosition
app:framePosition="50"
app:motionTarget="@id/moon"
app:keyPositionType="parentRelative"
app:percentY="0.5"
app:percentX="0.3"
/>
<KeyPosition
app:framePosition="75"
app:motionTarget="@id/moon"
app:keyPositionType="parentRelative"
app:percentY="0.6"
app:percentX="0.1"
/>
</KeyFrameSet>
KeyFrameSet之KeyCycle
这个是给动画添加运动波形的,比如正余弦,它的属性主要有以下几种
- motionTarget: 执行动画的view的id
- framePosition: 动画差值的位置0-100
- Standard View attributes: 透明、旋转、缩放、平移等属性(translationX)
- waveShape:产生的波形图{sin|square|triangle|sawtooth|reverseSawtooth|cos|bounce}
- wavePeriod: 波形在固定位置附近的循环周期
- waveOffset offset
- transitionPathRotate: 相对于view的移动路径的旋转
- progress: 调用view的setProgress方法 (used to talk to nested ConstraintLayouts etc.)
- CustomAttribute 通过反射调用view的setXXX方法 (仅限于值为float)
<KeyFrameSet>
<KeyCycle
android:translationY="50dp"
motion:framePosition="100"
motion:motionTarget="@id/button"
motion:waveOffset="0"
motion:wavePeriod="0"
motion:waveShape="bounce" />
<KeyCycle
android:translationY="50dp"
motion:framePosition="50"
motion:motionTarget="@id/button"
motion:waveOffset="0"
motion:wavePeriod="1"
motion:waveShape="bounce" />
<KeyCycle
android:translationY="50dp"
motion:framePosition="0"
motion:motionTarget="@id/button"
motion:waveOffset="0"
motion:wavePeriod="0"
motion:waveShape="bounce" />
</KeyFrameSet>
比如这里选用的是bounce,运动轨迹如下






