最新的動畫布局來了,一文帶你瞭解!
BATcoder技術 群,讓一部分人先進大廠
大家好,我是劉望舒,騰訊最具價值專家,著有三本業內知名暢銷書,三本書被中國國家圖書館、各大985名校圖書館收藏,連續五年蟬聯電子工業出版社年度優秀作者。
前華為面試官、獨角獸公司技術總監。
想要 加入 BATcoder技術群,公號回覆 BAT
即可。
作者:android超級兵
http://blog.csdn.net/weixin_44819566
前言
環境
-
system : macOS
-
android studio : 4.1.3
-
constraintLayout : 2.0.4
-
gradle : gradle-6.7.1-bin
-
kotlin : 1.4.23
你需要知道什麼
MotionLayout是ConstraintLayout的子類,具有ConstraintLayout所有功能。
implementation 'androidx.constraintlayout:constraintlayout: 2.0 .4'
使用as預覽MotionLayout,as版本必須 >= 4.0。
正文
使用 MotionLayout 建立動畫
將原ConstraintLayout轉換為MotionLayout佈局。
轉換之後:
可以看出,這裡定義了一個Button,如果說在ConstraintLayout,這樣寫會有警告,讓約束起來,但是這裡並沒有。
沒有的原因是因為定義了MotionLayout的layoutDescription屬性,需要在layoutDescription屬性中寫。
點選動畫OnClick
onClick#clickAction說明
motion:clickAction=“toggle”
效果:
motion:clickAction=“transitionToEnd”
motion:clickAction=“jumpToEnd”
motion:clickAction=“jumpToStart”
不新增動畫到開始。
motion:clickAction=“transitionToStart”
新增動畫到開始。
多個view一起聯動
例如這樣:
效果圖:
手指拖動OnSwipe
引數介紹:
-
motion:touchAnchorId 指的是您可以滑動並拖動的檢視。
-
motion:touchAnchorSide 表示我們從右側拖動檢視。
-
motion:dragDirection 表示拖動的進度方向。
例如,motion:dragDirection=“dragRight”表示當您向右拖動時,進度會增加。
效果圖:
輔助工具
motionLayout預設會自帶除錯工具。
官方說明圖:
-
圓圈代表一個檢視的開始或結束位置。
-
線條代表一個檢視的路徑。
-
菱形代表KeyPosition修改路徑。
還有一種使用檢視的方式來除錯:
修改路徑(KeyPosition)
修改路徑可以通過右側視覺化工具來進行:
詳解圖
再來看看自動生成的程式碼:
KeyPosition引數詳解:
-
motion:motionTarget="@+id/button" 需要移動軌跡的view
-
motion:framePosition="[0-100]" framePosition是一個介於 0 和 100 之間的數字。它定義了在動畫KeyPosition中的應用時間,1 表示 1% 的動畫,99 表示 99% 的動畫
-
motion:keyPositionType=“keyPositionType”這是如何KeyPosition修改路徑。它可以是parentRelative,pathRelative,或deltaRelative
-
percentX | percentY是修改路徑的量framePosition(值介於 0.0 和 1.0 之間,允許負值和值 >1)
這裡motion:keyPositionType引數解釋一下:
parentRelative
這裡設定了4個點,分別為:
假設現在移動起始點1,deltaRelative會以開始點和起始點2來構建一個“貝塞爾環境”來生成對應的路徑,這裡和pathRelative效果類似但也有不同之處!
pathRelative和deltaRelative的區別:
-
pathRelative不需要依靠起始點和結束點就可以拖動
-
deltaRelative 起始點和結束點在同一x軸上會導致動畫路徑不按照貝塞爾路徑執行
-
而是執行一條直線
最終效果長這樣:
效果都一樣,就只放一個嘍,需要的請下載原始碼觀看。
KeyPosition配合pathMotionArc進階
pathMotionArc見名之意就知道是用來畫弧形的。先來看pathMotionArc簡單的例子:
可以看出,想要畫一條優雅的弧線很簡單,只需要在開始點設定motion:pathMotionArc="startHorizontal"即可。
:warning: motion:pathMotionArc是需要2個點才生效的,預設是開始點和結束點
如何設定多個點呢?例如這樣,也可以設定兩個弧形的比例大小等。
位置座標說明:
pathMotionArc型別介紹:
-
startVertical 向下的弧形
-
startHorizontal 向上的弧形
-
none 直線
-
flip 和上一個點如果上一個點是startVertical,,那麼當前就是startHorizontal,可以理解為反轉
型別說明效果圖
開始點:motion:pathMotionArc="startVertical"
起始點1:motion:pathMotionArc="startVertical"
起始點2:motion:pathMotionArc=“flip”
效果程式碼:
<?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= "1000" >
<!-- 點選事件 -->
<OnClick
motion:clickAction= "toggle"
motion:targetId= "@id/button6" />
<KeyFrameSet>
<!-- 起始點 1 和 起始點 2 設定 "關聯" -->
<KeyPosition
motion:framePosition= "40"
motion:keyPositionType= "deltaRelative"
motion:motionTarget= "@+id/button6"
motion:pathMotionArc= "startVertical"
motion:percentX= "0.358"
motion:percentY= "0.17" />
<!-- 起始點 2 和結束關聯 -->
<KeyPosition
motion:framePosition= "79"
motion:keyPositionType= "deltaRelative"
motion:motionTarget= "@+id/button6"
motion:pathMotionArc= "flip"
motion:percentX= "0.675"
motion:percentY= "0.568" />
</KeyFrameSet>
</Transition>
<!-- start -->
<ConstraintSet android:id= "@+id/start" >
<!-- 開始點 和起始點 1 設定 "關聯" -->
<Constraint
android:id= "@+id/button6"
android:layout_width= "80dp"
android:layout_height= "80dp"
motion:layout_constraintBottom_toBottomOf= "parent"
motion:layout_constraintLeft_toLeftOf= "parent"
motion:layout_constraintTop_toTopOf= "parent"
motion:pathMotionArc= "startVertical" />
</ConstraintSet>
<!-- end -->
<!-- 結束點不設定關聯。。 -->
<ConstraintSet android:id= "@+id/end" >
<Constraint
android:id= "@+id/button6"
android:layout_width= "64dp"
android:layout_height= "64dp"
motion:layout_constraintBottom_toBottomOf= "parent"
motion:layout_constraintRight_toRightOf= "parent" />
</ConstraintSet>
</MotionScene>
這裡比例也是能改變的,例如這樣:
這裡還是比較簡單的,自己動手試試就懂啦!
改變屬性狀態(KeyAttribute)
看一眼程式碼:
<?xml version= "1.0" encoding= "utf-8" ?>
<MotionScene x<?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/結束了"
motion:constraintSetStart= "@id/開始了"
motion:duration= "1000" >
<KeyFrameSet>
<!-- 改變屬性狀態 -->
<KeyAttribute
motion:motionTarget= "@+id/button7"
motion:framePosition= "22"
android:alpha= "0.2" />
</KeyFrameSet>
<OnClick
motion:clickAction= "toggle"
motion:targetId= "@id/button7" />
</Transition>
<!-- 開始 這裡也可以定義中文,但是不建議使用。。 -->
<ConstraintSet android:id= "@+id/開始了" >
<Constraint
android:id= "@+id/button7"
android:layout_width= "60dp"
android:layout_height= "60dp"
motion:layout_constraintBottom_toBottomOf= "parent"
motion:layout_constraintLeft_toLeftOf= "parent"
motion:layout_constraintTop_toTopOf= "parent" />
</ConstraintSet>
<!-- 結束 這裡也可以定義中文,但是不建議使用。。-->
<ConstraintSet android:id= "@+id/結束了" >
<Constraint
android:id= "@+id/button7"
android:layout_width= "60dp"
android:layout_height= "60dp"
motion:layout_constraintBottom_toBottomOf= "parent"
motion:layout_constraintRight_toRightOf= "parent"
motion:layout_constraintTop_toTopOf= "parent" />
</ConstraintSet>
</MotionScene>
mlns: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= "1000" >
<!-- 點選事件 -->
<OnClick
motion:clickAction= "toggle"
motion:targetId= "@id/button6" />
<KeyFrameSet>
<!-- 起始點 1 和 起始點 2 設定 "關聯" -->
<KeyPosition
motion:framePosition= "40"
motion:keyPositionType= "deltaRelative"
motion:motionTarget= "@+id/button6"
motion:pathMotionArc= "startVertical"
motion:percentX= "0.358"
motion:percentY= "0.17" />
<!-- 起始點 2 和結束關聯 -->
<KeyPosition
motion:framePosition= "79"
motion:keyPositionType= "deltaRelative"
motion:motionTarget= "@+id/button6"
motion:pathMotionArc= "flip"
motion:percentX= "0.675"
motion:percentY= "0.568" />
</KeyFrameSet>
</Transition>
<!-- start -->
<ConstraintSet android:id= "@+id/start" >
<!-- 開始點 和起始點 1 設定 "關聯" -->
<Constraint
android:id= "@+id/button6"
android:layout_width= "80dp"
android:layout_height= "80dp"
motion:layout_constraintBottom_toBottomOf= "parent"
motion:layout_constraintLeft_toLeftOf= "parent"
motion:layout_constraintTop_toTopOf= "parent"
motion:pathMotionArc= "startVertical" />
</ConstraintSet>
<!-- end -->
<!-- 結束點不設定關聯。。 -->
<ConstraintSet android:id= "@+id/end" >
<Constraint
android:id= "@+id/button6"
android:layout_width= "64dp"
android:layout_height= "64dp"
motion:layout_constraintBottom_toBottomOf= "parent"
motion:layout_constraintRight_toRightOf= "parent" />
</ConstraintSet>
</MotionScene>
改變alpha值:
當然也可以設定多個屬性,例如這樣:
自行探索
-
android:visibility
-
android:alpha
-
android:elevation
-
android:rotation
-
android:rotationX
-
android:rotationY
-
android:scaleX
-
android:scaleY
-
android:translationX
-
android:translationY
-
android:translationZ
KeyAttribute配合CustomAttribute設定顏色
CustomAttribute常用來設定view顏色的,是KeyAttribute的屬性。來看看程式碼:
<?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= "1000" >
<!--點選-->
<OnClick
motion:clickAction= "toggle"
motion:targetId= "@id/imageView8" />
<KeyFrameSet>
<!-- 旋轉 3 圈到 100 點 -->
<KeyAttribute
android:rotation= "-720"
motion:framePosition= "100"
motion:motionTarget= "@id/imageView8" />
<!-- 0 點 的時候黑色 -->
<KeyAttribute
motion:framePosition= "0"
motion:motionTarget= "@id/imageView8" >
<CustomAttribute
motion:attributeName= "colorFilter"
motion:customColorValue= "#000000" />
</KeyAttribute>
<!-- 50 點的時候紅色 -->
<KeyAttribute
motion:framePosition= "50"
motion:motionTarget= "@id/imageView8" >
<CustomAttribute
motion:attributeName= "colorFilter"
motion:customColorValue= "#E91E63" />
</KeyAttribute>
<!-- 100 點的時候黑色 -->
<KeyAttribute
motion:framePosition= "100"
motion:motionTarget= "@id/imageView8" >
<CustomAttribute
motion:attributeName= "colorFilter"
motion:customColorValue= "#000000" />
</KeyAttribute>
</KeyFrameSet>
</Transition>
<ConstraintSet android:id= "@+id/start" ... />
<ConstraintSet android:id= "@+id/end" ... />
</MotionScene>
在裡面CustomAttribute你必須指定一個attributeName和一個值來設定。
motion:attributeName是此自定義屬性將呼叫的setter的名稱。在這個例子中, setColorFilteronDrawable將被呼叫。
motion:customColorValue是名稱中註明的型別的自定義值,在此示例中,自定義值是指定的顏色。
自定義值可以具有以下任何型別:
-
Color
-
Integer
-
Float
-
String
-
Dimension
-
Boolean
來看效果圖就懂了。
設定抖動[KeyCycle]
如何建立
關鍵程式碼
<Transition
motion:constraintSetEnd= "@+id/end"
motion:constraintSetStart= "@id/start"
motion:duration= "1000" >
<!--點選-->
<OnClick ... />
<KeyFrameSet>
<!--
motion:waveOffset 新增到屬性的偏移值
motion:wavePeriod 要在此區域附近迴圈的迴圈數
motion:waveShape= "cos"
sin|square|triangle|sawtooth|reverseSawtooth|cos|bounce
正弦| 方形 |三角形 | 鋸齒 | 反向鋸齒 |cos| 反彈
-->
<KeyCycle
android:alpha= "0.5"
android:scaleY= "1.2"
android:scaleX= "1.2"
motion:framePosition= "51"
motion:motionTarget= "@+id/imageView8"
motion:waveOffset= "2"
motion:wavePeriod= "1"
motion:waveShape= "sin" />
</KeyFrameSet>
</Transition>
引數介紹:
-
motion:waveOffset 新增到屬性的偏移值
-
motion:wavePeriod 要在此區域附近迴圈的迴圈數
-
motion:waveShape=“cos”
-
sin|square|triangle|sawtooth|reverseSawtooth|cos|bounce
各大型別效果圖:
(具體效果可以去原文進行檢視)
設定抖動(KeyTimeCycle)
KeyTimeCycle和KeyCycle使用起來是一樣的,引數也是一樣的。
有一點不同的是,一般KeyTimeCycle是三個一起使用,通過3個KeyTimeCycle定義一個準確的迴圈關鍵幀。
可以看出,只有在第50幀的時候,會發生改變,因為第50幀的時候設定motion:wavePeriod為1。
-
motion:wavePeriod:要在此區域附近迴圈的迴圈數
改變控制元件屬性(KeyTrigger)
什麼叫改控制元件屬性?如何控制?
首先自定義ImageView,裡面就2個方法,show和hide。
class KeyTriggerImageView @ JvmOverloads constructor (
context : Context , attrsAttributeSet
= null , defStyleAttr: Int = 0) :
AppCompatImageView(context, attrs, defStyleAttr) {
// 顯示 view
fun show () {
visibility = View.VISIBLE
}
// 隱藏當前 view
fun hide () {
visibility = View.GONE
}
}
如何使用:
<?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= "2000" >
<!--點選-->
<OnClick
motion:clickAction= "toggle"
motion:targetId= "@id/imageView11" />
<KeyFrameSet>
<KeyTrigger
motion:framePosition= "0"
motion:motionTarget= "@+id/imageView11"
motion:onCross= "show" />
<KeyTrigger
motion:framePosition= "20"
motion:motionTarget= "@+id/imageView11"
motion:onCross= "hide" />
<KeyTrigger
motion:framePosition= "60"
motion:motionTarget= "@+id/imageView11"
motion:onCross= "show" />
<KeyTrigger
motion:framePosition= "79"
motion:motionTarget= "@+id/imageView11"
motion:onCross= "hide" />
<KeyTrigger
motion:framePosition= "100"
motion:motionTarget= "@+id/imageView11"
motion:onCross= "show" />
</KeyFrameSet>
</Transition>
<ConstraintSet android:id= "@+id/start" .../>
<ConstraintSet android:id= "@+id/end" .../>
</MotionScene>
KeyTrigger引數介紹:
-
motion:onCross 呼叫的方法名字
-
motion:framePosition 當前是第幾幀 (0-100)
-
motion:motionTarget 設定的控制元件id
來看一眼效果:
根據這個思路,是不是就可以在滑動的過程中替換圖示顯示,例如這樣:
再來一張輔助圖,現在應該是非常清晰了!!
加速與減速(Easing)
也是一個引數的效果,使用很簡單,直接看程式碼:
<?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= "1000" >
<!--點選-->
<OnClick
motion:clickAction= "toggle"
motion:targetId= "@id/imageView13" />
<KeyFrameSet>
</KeyFrameSet>
</Transition>
<!-- 開始 -->
<ConstraintSet android:id= "@+id/start" >
<!--
motion:transitionEasing 設定加速度 or 減速 型別
-->
<Constraint
android:id= "@+id/imageView13"
android:layout_width= "100dp"
android:layout_height= "100dp"
motion:layout_constraintBottom_toBottomOf= "parent"
motion:layout_constraintLeft_toLeftOf= "parent"
motion:transitionEasing= "acclerate" />
</ConstraintSet>
<!-- 結束-->
<ConstraintSet android:id= "@+id/end" >
<Constraint
android:id= "@+id/imageView13"
android:layout_width= "100dp"
android:layout_height= "100dp"
motion:layout_constraintRight_toRightOf= "parent"
motion:layout_constraintTop_toTopOf= "parent" />
</ConstraintSet>
</MotionScene>
實戰
先來看看實戰效果:
佈局長這樣:
需求分析:
-
類似皮皮蝦點選評論功能
-
點選評論按鈕時候,圖片縮小,底下彈出一個recyclerview來顯示評論
-
recyclerview顯示的時候,評論按鈕不顯示
-
recyclerview不顯示的時候,評論按鈕顯示
activity_motion_layout_9_scene.xml
<?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= "400" >
<OnClick
motion:clickAction= "toggle"
motion:targetId= "@id/imageComment" />
<OnSwipe
motion:dragDirection= "dragUp"
motion:touchAnchorId= "@id/recyclerView" />
</Transition>
<!-- 開始 -->
<ConstraintSet android:id= "@+id/start" >
<Constraint
android:id= "@+id/image"
android:layout_width= "match_parent"
android:layout_height= "match_parent"
motion:layout_constraintBottom_toBottomOf= "parent"
motion:layout_constraintLeft_toLeftOf= "parent"
motion:layout_constraintRight_toRightOf= "parent"
motion:layout_constraintTop_toTopOf= "parent" />
<Constraint
android:id= "@+id/imageComment"
android:layout_width= "40dp"
android:layout_height= "40dp"
android:layout_marginRight= "@dimen/dp_10"
android:alpha= "1"
motion:layout_constraintBottom_toBottomOf= "parent"
motion:layout_constraintRight_toRightOf= "parent"
motion:layout_constraintTop_toTopOf= "parent"
motion:layout_constraintVertical_bias= "0.7" />
<Constraint
android:id= "@+id/recyclerView"
android:layout_width= "match_parent"
android:layout_height= "0dp"
motion:layout_constraintTop_toBottomOf= "@id/image" />
</ConstraintSet>
<!-- 結束 -->
<ConstraintSet android:id= "@+id/end" >
<Constraint
android:id= "@+id/image"
android:layout_width= "match_parent"
android:layout_height= "0dp"
motion:layout_constraintBottom_toTopOf= "@id/recyclerView"
motion:layout_constraintLeft_toLeftOf= "parent"
motion:layout_constraintRight_toRightOf= "parent"
motion:layout_constraintTop_toTopOf= "parent" />
<Constraint
android:id= "@+id/recyclerView"
android:layout_width= "match_parent"
android:layout_height= "500dp"
motion:layout_constraintBottom_toBottomOf= "parent"
motion:layout_constraintTop_toBottomOf= "@id/image" />
<Constraint
android:id= "@+id/imageComment"
android:layout_width= "40dp"
android:layout_height= "40dp"
android:layout_marginRight= "@dimen/dp_10"
android:alpha= "0"
motion:layout_constraintBottom_toBottomOf= "parent"
motion:layout_constraintRight_toRightOf= "parent"
motion:layout_constraintTop_toTopOf= "parent"
motion:layout_constraintVertical_bias= "0.7" />
</ConstraintSet>
</MotionScene>
走到這裡基本上就完事了。
/ 總結 /
大致結構:
<Transition
app:constraintSetStart= "@+id/start"
app:constraintSetEnd= "@+id/end"
app:duration= "1000" >
<!-- 拖動 -->
<OnSwipe />
<!-- 點選 -->
<OnClick />
<KeyFrameSet >
<KeyAttribute>
<CustomAttribute/>
</KeyAttribute>
<KeyPostion/>
<KeyCycle/>
<KeyTimeCycle/>
</KeyFrameSet>
<!--用於過渡動畫的起始點狀態引數配置-->
<Constraint android:id= "@id/viewId" >
<!-- 運動模型: 弧線路路徑,時間模型等 -->
<Motion/>
<!--
佈局相關
注意: width 、 height 和 margin 的名稱空間是 android: (beta1 開始)
而約束相關的名稱空間是 app (或 motion )
-->
<Layout/>
<!-- 動畫變換:做旋轉,位移,縮放,海海拔等屬性 -->
<Transform/>
<!--
自定義屬性
attributeName 會加上 set/get 反射找到真正的函式名,
⽐如 backgroundColor 就會呼叫 setBackgroundColor() 函式
custom(xxx)Value 對應屬性的資料型別
-->
<CustomAttribute/>
<!--
特定的屬性
visibility 、alpha 等屬性
-->
<PropertySet/>
</Constraint>
<!--用於過渡動畫的結束點狀態引數配置-->
<ConstraintSet android:id= "@+id/end" ../>
</Transition>
完整程式碼地址:
http://gitee.com/lanyangyangzzz/android_ui/tree/master
為了防止失聯,歡迎關注我的小號
微信改了推送機制,真愛請星標本公號 :point_down:
- 厲害了!自己寫個App 啟動任務框架
- 一個解決滑動衝突的新思路,做到檢視之間無縫地巢狀滑動!
- 谷歌官方改了兩次的知識點,你一定要知道!
- Android 最新架構詳解 | MVI = 響應式程式設計 單向資料流 唯一可信資料來源 !
- 說兩件事~
- 最新的動畫布局來了,一文帶你瞭解!
- Gradle:你必須掌握的開發常見技巧~
- Kotlin DSL 實戰:像 Compose 一樣寫程式碼!
- 厲害了,Android自定義樹狀圖控制元件來了!
- 一文帶你全面掌握Android元件化核心!
- 為什麼大廠開始全面轉向Compose?
- 谷歌限制俄羅斯使用Android系統,俄或將轉用 HarmonyOS!
- 鴻蒙OS、安卓、iOS測試對比,結果出乎意料!
- 最詳細的Android圖片壓縮攻略,讓你一次過足癮(建議收藏)
- Android字型漸變效果實戰!
- 攔截控制元件點選 - 巧用ASM處理防抖!
- Android正確的保活方案,拒絕陷入需求死迴圈!
- 再見 MMKV,自己擼一個FastKV,快的一批
- 白嫖一個Android專案的類圖生成工具!(建議收藏)
- 日常需求做的挺好,面試就被底層原理放倒