MotionLayout
作為ConstraintLayout
子類,在ConstraintLayout 2.0
庫被引入,主要用來管理運動和組件的動畫。ConstrantLayout
約束佈局,用過的人都説好,反正我用着挺爽的。有部分同學説性能問題,其實對於初中級開發者來説,暫無需考慮這個,相比自己動手嵌套幾層佈局強吧,而且更重要的是業務UI的實現,尤其工作量大的時候。
本文屬於入門級別,重點在於掃盲和入門。如果對你有用,歡迎點贊。個人能力有限,有些東西可能理解不透或不對,歡迎指正,非常感謝。
配置
需要將ConstraintLayout
的版本升級到2.0+。
AndroidX:
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta8'
複製代碼
支持庫:
implementation 'com.android.support.constraint:constraint-layout:2.0.0-beta8'
複製代碼
學習MotionLayout
動畫可能需要點Transition
和ConstraintLayout
知識點,不瞭解可以看看文末鏈接哦。MotionLayout
運動動畫定義了在兩個狀態集(StateSet)或者兩個約束集(ConsraintSet
)之間如何進行過渡。狀態集與約束集只是過渡動畫不同的組織方式。
如果快速入手
1、通過Android Studio創建名為activity_motion
的MotionLayout
佈局文件。
MotionLayout
佈局後會報紅,提示創建MotionScene.xml
文件。
3、選擇創建後,會在res/xml
文件夾下生成activity_motion_scene.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/widget" />
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint android:id="@id/widget" />
</ConstraintSet>
<Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@+id/start" />
</MotionScene>
複製代碼
此時在activity_motion.xml
佈局文件中的MotionLayout
標籤會多一個layoutDescription="@xml/activity_motion_scene"
屬性,但Android Studio還是缺少layoutDescription
屬性的錯誤,需要手動添加上命名空間。
<?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_scene">
</androidx.constraintlayout.motion.widget.MotionLayout>
複製代碼
歐力給,已經學會創建MotionLayout
,但好像沒什麼卵用。我們來看看剛剛自動生成的activity_motion_scene.xml
文件。
【劃重點】根標籤MotionScene
有一個defaultDuration
屬性,表示所有未指定時間的動畫的默認時間,默認為300毫秒。MotionScene
根標籤 必須包含Transition
標籤,可以有多個Transition
標籤。Transition
標籤是用來指定動畫的開始和結束狀態、任何中間狀態以及觸發動畫的動作,可以理解為一個Transition
標籤對應一個動畫。同時,MotionScene
標籤可以包含TransitionSet
標籤,這是可選的。TransitionSet
標籤主要為Transition
標籤提供起始和結束狀態的位置和屬性。而TransitionSet
標籤必須包含一個或多個Constraint
子標籤。Constraint
標籤用來定義佈局中某個View
在動畫中某個狀態下位置(通過ConstraintLayout
的相關屬性來約束)。
充分理解上段話的內容,下面通過實戰加深理解:
1、在activity_motion.xml
佈局文件增加一個id
為vStartStatus
的正方形View
。並在根標籤MotionLayout
添加showPaths="true"
屬性,用來顯示正方形運動的路徑。
<?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_scene"
app:showPaths="true">
<View
android:id="@+id/vStartStatus"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@color/colorPrimary" />
</androidx.constraintlayout.motion.widget.MotionLayout>
複製代碼
2.將activity_motion_scene.xml
文件中Constraint
標籤的id
值修改成正方形的id
,即vStartStatus
。Constraint
標籤的id
屬性值需要與要起動畫效果的View
的id
保持一致,這樣Constraint
標籤的所有屬性都會作用於該View
。Constraint
標籤的屬性與ConstraintLayout
的屬性是一致的,為此,給正方形開始狀態增加一些屬性,使其位置水平居中,距離頂部50dp
。
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/vStartStatus"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="50dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
</Constraint>
</ConstraintSet>
複製代碼
因為id
為start
的ConstraintSet
標籤關聯到Transition
標籤的constraintSetStart
屬性,所以它作為動畫(目前只有一個動畫)的起始狀態。而id
為end
的ConstraintSet
標籤關聯到Transition
標籤的constraintSetEnd
屬性,所以它將作為動畫的結束狀態。結束狀態我們將正方形設置水平居中,距離底部50dp
。
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@id/vStartStatus"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginBottom="50dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
</Constraint>
</ConstraintSet>
複製代碼
設置Constraint
標籤時記得設置layout_width
與layout_height
,不然是看不到正方形的。
- 到這一步,
Transition
標籤已經擁有開始和結束狀態了,就差觸發動畫開始的操作了。給Transition
標籤增加onClick
子標籤,表示點擊觸發動畫。onClick
標籤增加clickAction
屬性,值為toggle
,表示重複點擊時,動畫循環效果;增加targetId
屬性,值為@id/vStartStatus
,表示點擊正方形視圖觸發過渡動畫。
<Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@+id/start">
<OnClick
app:clickAction="toggle"
app:targetId="@id/vStartStatus" />
</Transition>
複製代碼
此時activity_motion_scene.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"
app:defaultDuration="500">
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/vStartStatus"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="50dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@id/vStartStatus"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginBottom="50dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
</Constraint>
</ConstraintSet>
<Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@+id/start">
<OnClick
app:clickAction="toggle"
app:targetId="@id/vStartStatus" />
</Transition>
</MotionScene>
複製代碼
效果圖
OK,看到這裏,你應該可以創建個類似的MotionLayout動畫。還不行的話,需要回頭再看看。下面講介紹一些標籤的屬性與效果。
標籤與屬性
Transition標籤
Transition
標籤主要用來指定Motion場景中一個或多個動畫。即關聯到動畫對應的各種狀態和用户交互動作。和過渡動畫是大同小異的。
常用屬性:
constraintSetStart
:指定動畫初始狀態;
constraintSetEnd
:指定動畫結束狀態;
duration
:指定動畫時長;
autoTransiton
:是否自動開啟動畫。取值有:animateToStart
過渡到初始狀態、animateToEnd
過渡到結束狀態、jumpToEnd
跳到結束狀態、jumpToStart
跳到初始狀態、none
不開始狀態。默認情況下是none
,當設為其他值時,不用和用户交互即自動開啟動畫。
motionInterpolator
:插值器。取值有:linear
線性、bounce
彈簧、easeIn
淡入、easeOut
淡出、easeInOut
淡入淡出;
transitionDisable
:允許動畫功能。取值:false
和true
;
layoutDuringTransition
:動畫過程中,MotionLayout
子View
調用reqeustLayout
,是否做出響應。取值honorRequest
響應、ignoreRequest
忽略;(beta 4)
1、用户交互的子標籤
Transition
標籤通過一些子標籤,實現與用户交互的行為。例如上文的OnClick
子標籤表示用户的點擊行為。
OnClick
標籤:點擊場景中某個視圖,開始動畫效果。
OnSwipe
標籤:表示在佈局上滑動時要執行的操作。由於個人能力有限,一些屬性不能準備表達。
2、關鍵幀子標籤
在上文中,默認情況下過渡動畫Transition
標籤會關聯一個開始狀態和一個結束狀態的TransitionSet
標籤。但我們知道Transition
標籤不僅可以創建初始狀態和結束狀態,還可以創建中間狀態。這些中間狀態則由關鍵幀來構成,以實現更復雜的動畫效果。
KeyFrameSet
標籤:用來指定某個中間狀態的位置和屬性。其實和過渡動畫的關鍵幀是一樣的概念。KeyFrameSet
標籤含有KeyPosition
和KeyAttribute
兩個子標籤,這些共同構成過渡動畫過程中某特殊狀態的位置和屬性。
位置關鍵幀
KeyPosition
標籤:
用來定義整個運動動畫中某個狀態的位置,相比於靜態的TransitionSet
標籤來説,更加靈活。
framePosition
:當前關鍵幀的位置,把整個運動動畫分成100個位置,取值0到99,那麼初始狀態的位置就是0,結束狀態就是99。
keyPositionType
:參考座標系的選擇,決定了percentX
和percentY
屬性取值的結果。
取值:parentRelative
表示座標系基於父視圖。例如在開頭的demo,加上下面的關鍵幀:
<KeyFrameSet>
<KeyPosition
app:percentY="0.5"
app:framePosition="50"
app:motionTarget="@id/vStartStatus"
app:keyPositionType="parentRelative"
app:percentX="0.25" />
</KeyFrameSet>
複製代碼
代碼定義了運動動畫過程的中間位置framePosition="50"
,參考系選擇了相對父視圖座標keyPositionType="parentRelative"
。由於父視圖是全屏,所以座標系原點在屏幕的左上角,percentY="0.5"
和percentX="0.25"
則表示正方形在父視圖高度的1/2,寬度1/4的位置。
效果圖
將keyPositionType
屬性改為deltaRelative
,即座標系選擇參照整個過渡動畫的位置,那麼起始狀態的位置就是原點(0,0),結束狀態的位置就是終點(1,1)。
這裏由於原點和終點在x軸上的距離是0,所以percentX="0.25"
是沒有效果的。
效果圖:
將keyPositionType
屬性改為pathRelative
,即座標系選擇參照整個運動路徑,即起始和終點的直線距離構成X軸,此時y軸就有正負之分,表示在X軸的左邊還是右邊。x軸和y軸的長度都是等於路徑的長度。
例如代碼如下:
<KeyFrameSet>
<KeyPosition
app:framePosition="50"
app:percentX="0.5"
app:percentY="0.1"
app:keyPositionType="pathRelative"
app:motionTarget="@id/vStartStatus" />
</KeyFrameSet>
複製代碼
效果圖:
代碼如下,percentY
改為-1
:
<KeyFrameSet>
<KeyPosition
app:framePosition="50"
app:percentX="0.5"
app:percentY="-0.1"
app:keyPositionType="pathRelative"
app:motionTarget="@id/vStartStatus" />
</KeyFrameSet>
複製代碼
效果圖:
percentWidth
和percentHeight
屬性表示視圖自身大小,如果整個動畫過程中,視圖大小不存在變化,是沒有效果的。例如文章開始的demo就是沒有效果的,可以將正方形在起始狀態和結束狀態的大小改為不一致,就可以看到效果。percentWidth
和percentHeight
屬性會導致sizePercent
屬性失效。
屬性關鍵幀
KeyAttribute
相對於位置關鍵幀,屬性關鍵幀更注重的是屬性,而不是某位置。例如常見的位移、旋轉動畫。
屬性有:
framePosition
關鍵幀位置motionTarget
關聯視圖IdtransitionEasing
動畫速度curveFit
選擇基於直線的路徑或基於單一速率的路徑motionProgress
設置動畫進度android:alpha
透明度android:elevation
陰影,注意SDK版本android:rotation
旋轉android:rotationX
繞X軸旋轉"android:rotationY"
繞Y軸旋轉android:transformPivotX
旋轉或縮放的中心點X座標android:transformPivotY
旋轉或縮放的中心點Y座標transitionPathRotate
android:scaleX"
X軸縮放android:scaleY"
Y軸縮放android:translationX
X軸平移android:translationY
Y軸平移android:translationZ
X軸平移
如果以上屬性不夠,也可以通過添加CustomAttribute
子標籤實現自己屬性,跟屬性動畫自定屬性是同個概念。
自定義屬性
CustomAttribute
標籤必須通過attributeName
屬性指定一個屬性名。支持下類型的屬性。
customColorValue
顏色值類型customColorDrawableValue
顏色值的Drawable類型customIntegerValue
int類型customFloatValue
float類型customStringValue
String類型customDimension
尺寸類型customPixelDimension
Pixel尺寸類型customBoolean
Boolean 類型
到這裏,Transition
標籤和其子標籤、相關屬性基本就介紹完了。
ConstraintSet標籤
ConstraintSet
約束集主要用來定義多個屬性集合,並通過id被Transition
標籤引用,作為運動動畫過程的起始或結束狀態。
Constraint標籤
子標籤Constraint
用來該狀態某個View
的相關約束屬性,約束屬性支持ConstraintLayout
佈局的所有屬性+上文提到的自定義屬性。或者通過組織Layout
、PropertySet
、Transform
、Motion
、CustomAttribute
等子標籤,關於這些子標籤,感興趣可以參閲官方文檔
【系列好文推薦】
【參考文章】
【點贊,方便日後查閲;點贊,碼字不易】