Android MotionLayout動畫:續寫ConstraintLayout新篇章

2021-02-14 鴻洋

連結:

https://juejin.im/post/6854573206653812743

本文由作者授權發布。

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布局文件。

2、 生成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,不然是看不到正方形的。

3. 到這一步,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 關聯視圖Id

transitionEasing 動畫速度

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等子標籤,關於這些子標籤,感興趣可以參閱官方文檔:

https://developer.android.com/reference/androidx/constraintlayout/motion/widget/MotionLayout#motion

系列好文推薦:

Android屬性動畫,看完這篇夠用了吧

https://juejin.im/post/6846687601118691341

Android矢量圖動畫:每人送一輛掘金牌小黃車

https://juejin.im/post/6847902224396484621

Android過渡動畫,發現掘金小秘密

https://juejin.im/post/6850037271714856968

官方文檔

https://developer.android.com/reference/androidx/constraintlayout/motion/widget/MotionLayout#motion

中文官方文檔

https://developer.android.com/training/constraint-layout/motionlayout/ref/constraint

最後推薦一下我做的網站,玩Android: wanandroid.com ,包含詳盡的知識體系、好用的工具,還有本公眾號文章合集,歡迎體驗和收藏!

推薦閱讀:

掃一掃 關注我的公眾號

如果你想要跟大家分享你的文章,歡迎投稿~

┏(^0^)┛明天見!

相關焦點

  • MotionLayout系列之配合布局CoordinatorLayout, DrawerLayout, ViewPager使用
    在 Coordinatorlayout 中使用 MotionLayout:( MotionLayout 可以實現類似 CoodinatorLayout 的功能,我們將在以後的文章中提供示例)可以通過 MotionLayout 指定一部分 View 的動畫,將更多有趣的動畫加到已經存在的布局中。
  • 帶你了解 Android 約束布局 ConstraintLayout
    android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="水平居中"        app:layout_constraintLeft_toLeftOf="parent"        app:layout_constraintRight_toRightOf
  • ConstraintLayout 介紹
    layout_constraintRight_toLeftOflayout_constraintRight_toRightOflayout_constraintTop_toTopOflayout_constraintTop_toBottomOf
  • 強大的 ConstraintLayout
    :constraint-layout:1.0.2'<android.support.constraint.ConstraintLayout   android:layout_width="match_parent"   android:layout_height="match_parent"></android.support.constraint.ConstraintLayout
  • Android ConstraintLayout使用指南
    ConstraintLayout最低兼容Android 2.3;目前Android Studio 2.3默認使用ConstraintLayout作為布局文件的根布局;想要使用ConstraintLayout,需在項目的build.gradle添加com.android.support.constraint:constraint-layout:XXX版本號依賴
  • Android ConstraintLayout約束布局可視化工具使用~
    :constraint-layout:1.0.2'<android.support.constraint.ConstraintLayoutandroid:layout_width="match_parent"
  • Google 開源的 Android 排版庫:FlexboxLayout
    的一種布局,但是要比 Linearlayout 要強大的多。    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
  • Android 8.0 LinearLayout 源碼解析
    2.2 使用 weight 的情況2.2.1 布局文件 & 效果上面分析了不使用 android:layout_weight 的情況,現在來分析下使用 android:layout_weight 的情況,還是通過一個例子入手,xml 布局如下所示<?
  • 是時候讓 Android Tools 屬性拯救你了
    android:layout_height="38dp"            app:layout_constraintStart_toStartOf="parent"            android:layout_marginStart="16dp"            android:layout_marginTop="16dp"
  • Android 自定義View 點讚效果
    ><android.support.constraint.ConstraintLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:agreeview="http://schemas.android.com/apk/res-auto"    xmlns:tools
  • Android開發:如何在MotionLayout中使用動態數據
    可以使用MotionLayout創建數據的動態動畫嗎?這是任何你在編譯時不知道的數據,比如用戶輸入。對!當然。可以使用MotionLayout代碼API在代碼中動態創建MotionScene。在本文中,我們將使用開發一個動態直方圖,該直方圖使用MotionLayout設置其更改的動畫。
  • Android 這些 Drawable 你都會用嗎?
    app:layout_constraintBottom_toBottomOf="parent"        app:layout_constraintLeft_toLeftOf="parent"        app:layout_constraintRight_toRightOf="parent"        app:layout_constraintTop_toTopOf
  • 【精講】CoordinatorLayout與滾動的處理
    浮動操作按鈕與SnackbarCoordinatorLayout可以用來配合浮動操作按鈕的 layout_anchor 和 layout_gravity屬性創造出浮動效果,layout_anchor 指定參照物, anchorGravity 指定相對於參照物的位置,設置為 bottom
  • 輕鬆掌握RelativeLayout相對布局
    android:layout_centerHorizontal:控制該組件是否和布局容器的水平居中。android:layout_centerVertical:控制該組件是否和布局容器的垂直居中。android:layout_centerInparent:控制該組件是否和布局容器的中央位置。
  • Android子線程也能修改UI?
    ><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http
  • 我用ConstraintLayout寫了個登錄頁面,原來是那麼的好用
    app:layout_constraintBottom_toBottomOf="@+id/view1"與之不同的是RelativeLayout,ConstraintLayout提供bias用於相對於手柄以0%和100%水平和垂直偏移定位視圖的值(用圓圈標記)。這些百分比(和分數)提供了跨不同屏幕密度和大小的視圖的無縫定位。
  • Android RecyclerView自定義LayoutManager
    addView(view);        measureChildWithMargins(view, 0, 0);        int width = getDecoratedMeasuredWidth(view);        int height = getDecoratedMeasuredHeight(view);        layoutDecorated
  • android 從後臺啟動頁面專題及常見問題 - CSDN
    layout.xml首先創建此頁面的布局文件:<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
  • 通關Android Lint
    4) UseSparseArrays 儘量用Android的SparseArray代替Hashmap 5) DisableBaselineAlignment 如果LinearLayout被用於嵌套的layout空間計算,它的android:baselineAligned屬性應該設置成false,以加速layout計算。
  • IC Layout 腳本分享
    hlist使用方法:需要把cds.lib 拷貝到與腳本相同的目錄layout_get_hlist.sh your.cdl腳本#2:名字:layout_ic61_strmout.sh語言:shell功能:用腳本導出GDS內容:#!