MotionLayout,字面翻譯是叫運(yùn)動布局芦疏,它是一個能夠幫助我們在 app 中管理手勢和控件動畫的布局組件放仗。它是 ConstraintLayout 的子類并且基于它自身豐富的布局功能來進(jìn)行構(gòu)建桐早。既然是Layout驻子,自然會聯(lián)想到線性布局等等這些进肯。也能通過XML配置來實(shí)現(xiàn)一些動畫效果艇肴。本文參考至https://juejin.im/post/5d595328f265da03c34bfa59
它具有ConstraintLayout的所有屬性腔呜。MotionLayout用來處理兩個ConstraintSet之間的切換,并在根據(jù)兩個ConstraintSet的CustomAttribute參數(shù)來自動生成切換動畫再悼,關(guān)于ConstraintSet下面會討論核畴。同時MotionLayout所增加的是可以直接通過觸摸屏幕來控制動畫的運(yùn)行進(jìn)度。也就是說MotionLayout會管理你的觸摸事件通過跟蹤手指的速度冲九,并將其與系統(tǒng)中的視圖速度相匹配谤草。從而可以自然地在兩者之間通過觸摸滑動平穩(wěn)過渡。并且在動畫里面加入了關(guān)鍵幀的概念莺奸,使得其自動生成動畫在運(yùn)行時某一階段會運(yùn)行到關(guān)鍵幀的狀態(tài)丑孩。同時MotionLayout支持在XML中完全描述一個復(fù)雜的動畫,而不需要通過Java代碼來實(shí)現(xiàn)
動畫效果如下憾筏。
我們先引入MotionLayout 庫
dependencies {
implementation 'com.android.support.constraint:constraint-layout:2.0.0-beta2'
}
我們在布局中使用
<android.support.constraint.motion.MotionLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/step"
app:motionDebug="SHOW_PATH"
>
<ImageView
android:id="@+id/ball"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/round"
/>
</android.support.constraint.motion.MotionLayout>
可以看出這個布局需要內(nèi)嵌一些控件嚎杨,比如imageView,等氧腰,同時最關(guān)鍵的屬性是需要配置動畫MotionScene xml文件枫浙。這就類似于我們平常自定義的drawable文件。app:motionDebug="SHOW_PATH" 調(diào)試屬性是可以預(yù)覽動畫的運(yùn)動軌跡古拴。那么接下來就進(jìn)入step文件看看箩帚,
<?xml version="1.0" encoding="utf-8"?>
<!--describe the animation for activity_motion_sample_step1.xml-->
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- A transition describes an animation via start and end state -->
<Transition
app:constraintSetStart="@id/start"
app:constraintSetEnd="@id/end"
app:duration="2200">
<OnClick
app:targetId="@id/ball"
app:clickAction="toggle" />
</Transition>
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/ball"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</ConstraintSet>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/ball"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</ConstraintSet>
</MotionScene>
因?yàn)镸otionLayout是繼承自ConstraintLayout,自然也有約束的屬性黄痪。所以包括ConstraintSet紧帕、Transition和StateSet。由于StateSet涉及篇幅較長桅打,這里暫沒有解釋是嗜。效果如下
由此可以看出,Transition控制了icon 的起始位置挺尾,constraintSetStart控制了ConstrainSet命名為start的標(biāo)簽鹅搪,即為開始位置,constraintSetEnd控制了ConstrainSet命名為end的標(biāo)簽遭铺,即為結(jié)束的位置丽柿。動畫持續(xù)時間為2200毫秒恢准。以及子屬性中的OnClick時間,綁定了xml布局中的id為ball的imageView甫题。余下的一看便知馁筐。
那么如果像更復(fù)雜的做法,多個icon從同一個起始位置開始坠非,不同地方結(jié)束呢敏沉。比如下面這樣
布局代碼是這樣
<android.support.constraint.motion.MotionLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/step2"
app:motionDebug="SHOW_PATH"
>
<ImageView
android:id="@+id/ic_android_blue"
android:layout_width="42dp"
android:layout_height="42dp"
android:src="@mipmap/round"/>
<ImageView
android:id="@+id/ic_android_left"
android:layout_width="42dp"
android:layout_height="42dp"
android:src="@mipmap/round"/>
<ImageView
android:id="@+id/ic_android_right"
android:layout_width="42dp"
android:layout_height="42dp"
android:src="@mipmap/round"/>
<TextView
android:id="@+id/tipText"
android:text="Swipe the blue android icon up"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="16dp"
android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.motion.MotionLayout>
MotionScene為這樣
<?xml version="1.0" encoding="utf-8"?>
<!--describe the animation for activity_motion_sample_step2.xml-->
<!--animate by dragging target view-->
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!--At the start, all three stars are centered at the bottom of the screen.-->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/ic_android_blue"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_marginBottom="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<Constraint
android:id="@+id/ic_android_left"
android:layout_width="42dp"
android:layout_height="42dp"
android:alpha="0.0"
android:layout_marginBottom="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<Constraint
android:id="@+id/ic_android_right"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_marginBottom="20dp"
android:alpha="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</ConstraintSet>
<!--Define the end constraint to set use a chain to position all three stars together below @id/tipText.-->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/ic_android_left"
android:layout_width="58dp"
android:layout_height="58dp"
android:layout_marginEnd="90dp"
android:alpha="1.0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/ic_android_blue"
app:layout_constraintTop_toBottomOf="@id/tipText"/>
<Constraint
android:id="@+id/ic_android_blue"
android:layout_width="58dp"
android:layout_height="58dp"
app:layout_constraintEnd_toStartOf="@id/ic_android_right"
app:layout_constraintStart_toEndOf="@id/ic_android_left"
app:layout_constraintTop_toBottomOf="@id/tipText"/>
<Constraint
android:id="@+id/ic_android_right"
android:layout_width="58dp"
android:layout_height="58dp"
android:layout_marginStart="90dp"
android:alpha="1.0"
app:layout_constraintStart_toEndOf="@id/ic_android_blue"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tipText"/>
</ConstraintSet>
<!-- A transition describes an animation via start and end state -->
<Transition
app:constraintSetStart="@id/start"
app:constraintSetEnd="@id/end">
<!-- MotionLayout will track swipes relative to this view -->
<OnSwipe app:touchAnchorId="@id/ic_android_blue"/>
</Transition>
</MotionScene>
可以看到原來布局中是可以出現(xiàn)放置多個動畫資源的,而ConstrainSet標(biāo)簽中也可以對不同資源進(jìn)行編輯不同的起始位置麻顶。而Transition中的OnSwipe標(biāo)簽更是可以為我們動畫增加多了滑動手勢的動作赦抖。
目前只是簡單的平移動作舱卡。如果是曲線動畫辅肾,當(dāng)然可以。KeyFrameSet轮锥,它可以改變我們動畫過程中某個關(guān)鍵幀的位置以及狀態(tài)信息矫钓。
<?xml version="1.0" encoding="utf-8"?>
<!--describe the animation for activity_motion_sample_step3.xml-->
<!--animate in the path way on a view-->
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@id/windmill"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="12dp"
android:layout_marginBottom="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<Constraint
android:id="@id/tipText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="@id/windmill"
app:layout_constraintTop_toTopOf="@id/windmill"/>
</ConstraintSet>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<!--this view end point should be at bottom of parent-->
<Constraint
android:id="@id/windmill"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginBottom="12dp"
android:layout_marginEnd="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<Constraint
android:id="@+id/tipText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:alpha="1.0"
android:layout_marginEnd="72dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</ConstraintSet>
<!-- A transition describes an animation via start and end state -->
<Transition
app:constraintSetStart="@id/start"
app:constraintSetEnd="@id/end">
<KeyFrameSet>
<KeyPosition
app:framePosition="50"
app:motionTarget="@id/windmill"
app:keyPositionType="parentRelative"
app:percentY="0.5"/>
<!--apply other animation attributes-->
<!--前半段的動畫效果:逆時針旋轉(zhuǎn)一圈,同時放大一倍-->
<KeyAttribute
app:motionTarget="@id/windmill"
android:rotation="-360"
android:scaleX="2.0"
android:scaleY="2.0"
app:framePosition="50"/>
<!--后半段的動畫效果:逆時針旋轉(zhuǎn)一圈舍杜,同時變回原樣-->
<KeyAttribute
app:motionTarget="@id/windmill"
android:rotation="-720"
app:framePosition="100"/>
<!--延遲動畫——0-85過程中將透明度一直維持在0.0-->
<KeyAttribute
app:motionTarget="@id/tipText"
app:framePosition="85"
android:alpha="0.0"/>
</KeyFrameSet>
<OnSwipe
app:touchAnchorId="@id/windmill"
app:touchAnchorSide="bottom"
app:dragDirection="dragRight"/>
</Transition>
</MotionScene>
可以看出里面最關(guān)鍵的新娜,KeyFrameSet 需要被包含在 Transition 里面,同時 KeyFrameSet 中定義了 <KeyPosition> 和 <KeyAttribute> 兩種元素既绩,它們主要用來設(shè)置動畫某個位置的關(guān)鍵幀概龄,進(jìn)而為某段動畫指定所期望的效果。顧名思義饲握,KeyPosition 用于指定動畫某個關(guān)鍵幀的位置信息私杜,而 KeyAttribute 則用來描述動畫某關(guān)鍵幀的屬性配置(如:透明度、縮放救欧、旋轉(zhuǎn)等)衰粹,這樣就能構(gòu)成一個比較有趣的動畫效果。當(dāng)然KeyFrameSet還有很多別的屬性笆怠。這里就不一一講述了铝耻。可以自己研究下蹬刷。