最新的動畫布局來了,一文帶你瞭解!

語言: CN / TW / HK

 BATcoder技術 群,讓一部分人先進大廠

大家好,我是劉望舒,騰訊最具價值專家,著有三本業內知名暢銷書,三本書被中國國家圖書館、各大985名校圖書館收藏,連續五年蟬聯電子工業出版社年度優秀作者。

前華為面試官、獨角獸公司技術總監。

想要 加入  BATcoder技術群,公號回覆 BAT  即可。

作者:android超級兵

https://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 (

contextContextattrs

AttributeSet

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>

完整程式碼地址:

https://gitee.com/lanyangyangzzz/android_ui/tree/master