MotionLayout
MotionLayout的介紹:
- 是什麼?
MotionLayout是一種佈局類型,能夠幫我們實現複雜的動畫效果,還能與人進行交互;
簡單來説,我們可以在佈局中定義多個狀態,Motionlayout幫助我們在這些狀態中平滑的過渡,從而實現複雜的動畫效果;
2. MotionLayout是ConstraintLayout的子類,所以具有ConstraintLayout類同樣強大的功能,所以我們只需要添加ConstraintLayout 的依賴,就可以使用 MotionLayout 了;
- 我們可以通過XML文件中聲明性的設置,就可以完成動畫效果了,當然我們也可以通過Java代碼設置,拿到
Motionlayout的實例,使用一些方法進行設置,後文會提到; MotionLayout只能為它直接子級設置動畫;
實現
添加依賴
implementation "androidx.constraintlayout:constraintlayout:2.2.0"
也可以去官網查看最新版
使用Motionlayout
方式一:
xml佈局中component Tree > 點擊第一個子項 > 點擊Convert to MotionLayout;
這種方法系統會自動把ConstraintLayout替換成MotionLayout,並且生成MotionScene;MotionScene可以在你的res/xml目錄下找到類似於nihao_scene.xml為名的文件;
方式二:
手動替換為MotionLayout,以及創建xml目錄下MotionScene為根佈局的文件;
MotionLayout文件如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:layoutDescription="@xml/motionsence"
android:background="@drawable/second">
<ImageView
android:id="@+id/page"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@drawable/first">
</ImageView>
</androidx.constraintlayout.motion.widget.MotionLayout>
佈局很簡單,MotionLayout佈局裏面寫了一個view;
MotionScene(運動性描述文件)
- 是什麼?
相應佈局的所有運動描述;也就是説我們定義什麼樣的動畫,就在這個文件裏設置; - 每個
MotionLayout都引用一個單獨的MotionScene;那麼怎麼將二者聯繫起來呢?
app:layoutDescription="@xml/motionsence" :通過MotionLayout的 app:layoutDescription聯繫起來;
MotionScene中的定義的運動描述優先於MotionLayout中的任何相同定義;比如定義寬高,最終顯示出來的效果是MotionScene中定義的寬高;
MotionLayout其他屬性:
showPaths:布爾類型,表示在運動進行時是否顯示運動路徑。默認值為 false;currentState:可指定具體的ConstraintSet,告訴MotionLayout從哪個狀態開始滑動;
其實重要的還是怎麼使用動畫達到我們想要的效果,所以我們來詳細看看MotionScene;
一個運動性描述文件框架上主要有Transition,ConstraintSet,Constraint,OnClick,OnSwipe;
簡單分析完成一段動畫需要什麼,我們得確定動畫的剛開始的狀態,結束時的狀態,怎麼觸發這個動畫,一個佈局裏面有很多個view,如何正確的把剛開始的狀態以及結束狀態應用到理想的view上;那麼我們圍繞以上內容進行學習;
ConstraintSet
- 介紹:
<ConstraintSet>元素是用來指定視圖在動畫序列中某一點上的位置和屬性(可同時指定多個視圖),也叫約束條件集合;
通俗來講:這就是決定動畫某一時刻(比如起始)的狀態,我們可以在這裏設置很多元素的起始狀態,所以這個就要做約束條件集合;
- 代碼模式:
<ConstraintSet android:id = "@+id/end">
<Constraint
...
</Constraint>
<Constraint
...
</Constraint>
</ConstraintSet>
- 屬性:
android:id = "@+id/end"指定這個約束集的名字;deriveConstraintsFrom:值為另一個ConstraintSet的 ID;如果指定這個屬性,ID 對應約束集合內的所有約束條件都將應用於此集合,除非此集明確替換它們;
- 裏面必須包含一個或者多個
Constraint元素;
Constraint
- 介紹:
<Constraint>元素用來聲明運動序列其中一個視圖的位置和屬性,也就是視圖的約束;
通俗來講:規定具體的view的位置以及屬性,為它設置狀態;
- 代碼:
<Constraint
android:id = "@+id/TODO"
android:layout_height="64dp"
android:layout_width="64dp"
motion:layout_constraintRight_toRightOf="parent"
motion:layout_constraintTop_toTopOf="parent"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp">
<CustomAttribute
motion:attributeName="background"
motion:customColorValue="#CCA4E3">
</CustomAttribute>
</Constraint>
- 屬性:
android:layout_height="64dp"這裏設置寬高的優先級高於view自身;這裏有一個疑問?為什麼在主佈局中定義了view的寬高,為什麼這裏還要再定義一遍?
答案:主佈局設置的是初始值,而這裏設置的是在這個動畫中這個狀態下的view的大小;
android:id = "@+id/TODO"這裏的ID不是為了起名字,而是為了指定作用於哪個view;
CustomAttribute
- 介紹:對view變化過程中進行插值,是運行時屬性,比如背景顏色,文字顏色,圓角半徑等;
- 代碼:
<CustomAttribute
motion:attributeName="backgroundColor"
motion:customColorValue="#D8C7D5">
</CustomAttribute>
- 屬性:
motion:attributeName你要變化的屬性名motion:customColorValue變化的數值
- 常見的屬性表如下:
|
屬性類型
|
對應的字段名
|
示例
|
説明
|
|
顏色
|
customColorValue
|
#FF4081
|
可對顏色進行漸變(自動補間 RGB)
|
|
浮點數
|
customFloatValue
|
0.5
|
常用於透明度、圓角半徑、進度等
|
|
整數
|
customIntegerValue
|
100
|
如寬高、邊距、進度值等
|
|
布爾
|
customBooleanValue
|
true
|
通常配合狀態切換使用
|
|
字符串
|
customStringValue
|
“Hello”
|
很少用於動畫,但可以保存狀態
|
|
維度
|
customDimension
|
16dp
|
常用於陰影半徑、圓角、邊距等
|
- 如果你要定義,那麼這個view的全程都應該寫
CustomAttribute;
Transition
- 介紹: 該元素是用來聲明運動過程中的開始和結束狀態,包括所有預期的過度狀態、用户的觸發的交互等。
通俗來講:我們可能會定義很多個狀態,那麼它就是將開始的狀態和結束的狀態連接到一起作為一個動畫,就是Transition的作用 - 代碼:
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/middle"
>
<OnClick
motion:targetId="@+id/TODO"
motion:clickAction="transitionToEnd">
</OnClick>
</Transition>
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/middle"
motion:duration="500">
<OnClick
motion:targetId="@+id/TODO"
motion:clickAction="transitionToEnd">
</OnClick>
</Transition>
- 屬性:
motion:constraintSetStart="@+id/start"確實運動過程中的起始狀態;motion:constraintSetEnd="@+id/middle"同理結束狀態;motion:duration="500"動畫持續的時間,如果沒寫,默認父元素中設置的時間,所以我們父元素也就是MotionScene中可以設置,屬性是motion:defaultDuration="1000"
Transition可以包含的元素:OnClick,OnSwipe,KeyFrameSet;
OnClick
- 介紹: 該元素用於指定當用户點按特定視圖時要執行的操作,指定當用户點擊視圖時觸發動畫序列;
通俗來講:就是點擊觸發動畫;
- 代碼
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/middle"
motion:duration="500">
<OnClick
motion:targetId="@+id/TODO"
motion:clickAction="transitionToEnd">
</OnClick>
</Transition>
- 屬性:
motion:targetId="@+id/TODO"你要滑動觸發的view的對象;motion:clickAction="transitionToEnd"代表點擊觸發的操作;
- clickAction屬性值附表
|
屬性值
|
含義
|
行為描述
|
典型使用場景
|
|
transitionToEnd
|
切換到 end 狀態 |
從當前狀態(通常是 start)平滑過渡到 end
|
點擊按鈕觸發開啓動畫,例如菜單展開、視圖滑出
|
|
transitionToStart
|
切換到 start 狀態 |
從當前狀態(通常是 end)回到start
|
點擊關閉按鈕,恢復初始位置
|
|
toggle
|
自動在 start / end 之間切換 |
如果當前在start 就去 end,反之返回 start
|
單個按鈕控制展開 / 收起
|
|
jumpToEnd
|
立即跳轉到 end 狀態 |
不進行動畫過渡,直接到結束佈局
|
測試時或某些需要立即切換的場景
|
|
jumpToStart
|
立即跳轉到 start 狀態 |
不進行動畫過渡,直接回到起始佈局
|
重置佈局
|
|
none
|
無操作
|
點擊無任何動畫或狀態變化
|
佔位、禁用點擊行為
|
其實jumpToStart和transitionToStart是區別就是有無動畫,下面是一個手動start和end之間切換的實例:
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/middle"
motion:duration="500">
<OnClick
motion:targetId="@+id/TODO"
motion:clickAction="transitionToEnd">
</OnClick>
</Transition>
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/middle"
motion:duration="500">
<OnClick
motion:targetId="@+id/TODO"
motion:clickAction="transitionToStart">
</OnClick>
</Transition>
我們可以看到兩個transition定義的起始和結束狀態都是一樣的,那麼設立了兩個點擊觸發,就可以達到點擊到結束狀態,再點擊回到初始狀態;
- 為什麼不寫在一個
Transition裏面?只能寫一個,寫多了編譯器報錯;
實現的效果如下:
OnSwipe
- 介紹: 該元素用於指定當用户在不居中滑動時需要執行的操作;
通俗來講:就是滑動觸發動畫;
- 代碼
<OnSwipe
motion:touchAnchorId="@id/page"
motion:touchRegionId="@id/page"
motion:dragDirection="dragLeft"
motion:onTouchUp="autoComplete"
>
</OnSwipe>
- 屬性:
motion:touchAnchorId="@id/page"你的目標view的ID;motion:touchRegionId="@id/page"你的錨點區域;motion:dragDirection="dragLeft"你朝哪個方向滑動觸發motion:onTouchUp="autoComplete"觸摸結束之後的動畫
實現的效果如下:
- 其他屬性:
motion:dragScale:控制目標視圖的滑動距離和用户手指滑動距離的相對比例,默認值是1。通俗來講,取值小於1時view比手滑動的慢,取值大於1時view比手快;
motion:maxVelocity:目標view的最大速度;
motion:maxAcceleration:目標view的最大加速度;
motion:onTouchUp屬性其他可選值:
stop(停止動畫)、autoComplete(自動完成動畫)、autoCompleteToEnd(自動完成到結束狀態)、autoCompleteToStart(自動完成到開始狀態)、decelerate(減速停止動畫)、decelerateAndComplete(減速並完成動畫);
KeyFrameSet
- 介紹:
<KeyFrameSet>就是提供聲明更加複雜動畫的元素,他聲明瞭動畫運動軌跡的關鍵點集合,平滑的連接這些關鍵點,可以得到動畫;
通俗來講:它是一個定義關鍵幀的容器,通過關鍵幀,可以精確定義動畫的軌跡和行為;
- 代碼
<KeyFrameSet>
<KeyPosition>
</KeyPosition>
<KeyAttribute>
</KeyAttribute>
</KeyFrameSet>
它有很多關鍵幀類型,每種類型有不同的屬性,觸發不同的事件,這個關鍵幀通過動畫的時間軸(frameposition)來定位;
關鍵幀`
KeyPosition
- 介紹:是一個關鍵幀類型,用於定義視圖在動畫路徑上的位置和大小變化;
- 代碼:
<KeyPosition
motion:framePosition="40"
motion:keyPositionType="parentRelative"
motion:motionTarget="@+id/TODO"
motion:percentX="0.45"
motion:percentY="0.75"
>
</KeyPosition>
- 屬性:
motion:framePosition="40"定義了動畫幀的位置,值為0-100,代表在動畫過程中的百分比位置;motion:motionTarget="@+id/TODO"目標viewmotion:percentX="0.45"取值在-1 - 1之間,座標軸取決於:keyPositionTypemotion:keyPositionType="parentRelative"計算關鍵幀的位置,有三種計算方式:
三種計算方式:
第一種parentRelative:相當於父容器來計算關鍵幀,左上角為(0,0);
``
第二種:deltaRelative: 基於相對於起始點和結束點的相對偏移量來計算路徑,起始座標和終點座標分別為(0,0)和(1,1)
第三種:以路徑為x軸,以垂直方向為Y軸
KeyAttribute
- 介紹:
<KeyAttribute>元素是用來在運動序列的特定時刻,設置視圖的任何標準屬性;
通俗來講:KeyPosition是規定view的位置和大小,KeyAttribute是規定view的UI元素屬性的關鍵幀類型;可以在動畫的某些時刻對元素的多個屬性進行變換,例如透明度、旋轉、縮放、平移等,從而實現複雜的動畫效果; - 代碼:
<KeyAttribute
motion:motionTarget="@+id/TODO"
motion:framePosition="50"
android:scaleX="1.5"
android:scaleY="1.5"
android:rotation="359"
>
</KeyAttribute>
- 屬性:
motion:motionTarget="@+id/TODO"目標viewmotion:framePosition="50"關鍵幀在動畫進度中的位置android:scaleX="1.5"表示將view的weight擴大到1.5倍android:scaleY="1.5"同理android:rotation="359"將view旋轉359度
- 其他屬性:
android:alpha設置透明度android:translationX、android:translationY:控制視圖的 X、Y 方向偏移;android:transformPivotX、android:transformPivotY:控制旋轉和縮放的中心點;transformPivotTarget: 指定其他視圖作為旋轉和縮放的中心點;motion:transitionEasing="easeInOut"設置速度
取值:加速(ease-in)減速(ease-out)先快後慢(easeInOut)
有這樣的效果:
代碼如下:
<?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"
motion:duration="2000">
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/middle"
>
<OnClick
motion:targetId="@+id/TODO"
motion:clickAction="transitionToEnd">
</OnClick>
<KeyFrameSet>
<KeyPosition
motion:framePosition="40"
motion:keyPositionType="parentRelative"
motion:motionTarget="@+id/TODO"
motion:percentX="0.45"
motion:percentY="0.75"
>
</KeyPosition>
<KeyPosition
motion:framePosition="50"
motion:keyPositionType="parentRelative"
motion:motionTarget="@+id/TODO"
motion:percentX="0.5"
motion:percentY="0.5"
>
</KeyPosition>
<KeyPosition
motion:framePosition="60"
motion:keyPositionType="parentRelative"
motion:motionTarget="@+id/TODO"
motion:percentX="0.65"
motion:percentY="0.45"
>
</KeyPosition>
<KeyAttribute
motion:motionTarget="@+id/TODO"
motion:framePosition="50"
android:scaleX="1.5"
android:scaleY="1.5"
android:rotation="359"
>
</KeyAttribute>
<KeyAttribute
motion:motionTarget="@+id/TODO"
motion:framePosition="100"
android:scaleX="1"
android:scaleY="1"
android:rotation="360"
>
</KeyAttribute>
</KeyFrameSet>
</Transition>
<ConstraintSet android:id = "@+id/start">
<Constraint
android:id = "@+id/TODO"
android:layout_height="100dp"
android:layout_width="100dp"
motion:layout_constraintLeft_toLeftOf="parent"
motion:layout_constraintBottom_toBottomOf="parent"
android:layout_marginLeft="10dp"
android:layout_marginBottom="10dp">
</Constraint>
</ConstraintSet>
<ConstraintSet android:id = "@+id/middle">
<Constraint
android:id = "@+id/TODO"
android:layout_height="100dp"
android:layout_width="100dp"
motion:layout_constraintRight_toRightOf="parent"
motion:layout_constraintBottom_toBottomOf="parent"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp">
</Constraint>
</ConstraintSet>
</MotionScene
動態設置
我們也可以在代碼中動態設置
|
方法
|
功能
|
|
transitionToEnd()
|
觸發當前 Transition 到 end 狀態
|
|
transitionToStart()
|
觸發當前 Transition 到 start 狀態
|
|
setProgress(float progress)
|
直接設置動畫進度,0~1
|
|
getProgress()
|
獲取當前 progress
|
|
setTransition(int startId, int endId)
|
動態切換當前 Transition
|
示例:
MotionLayout motionLayout = findViewById(R.id.motionLayout);
// 手動設置動畫進度到一半
motionLayout.setProgress(0.5f);
// 動態切換 Transition
motionLayout.setTransition(R.id.start, R.id.end);
// 播放動畫到結束
motionLayout.transitionToEnd();
好啦,本次介紹到此結束,謝謝大家;VV