MotionLayout

MotionLayout的介紹:

  1. 是什麼?
    MotionLayout是一種佈局類型,能夠幫我們實現複雜的動畫效果,還能與人進行交互;
    簡單來説,我們可以在佈局中定義多個狀態,Motionlayout幫助我們在這些狀態中平滑的過渡,從而實現複雜的動畫效果;

2. MotionLayoutConstraintLayout的子類,所以具有ConstraintLayout類同樣強大的功能,所以我們只需要添加ConstraintLayout 的依賴,就可以使用 MotionLayout 了;

  1. 我們可以通過XML文件中聲明性的設置,就可以完成動畫效果了,當然我們也可以通過Java代碼設置,拿到Motionlayout的實例,使用一些方法進行設置,後文會提到;
  2. MotionLayout 只能為它直接子級設置動畫;

實現

添加依賴

implementation "androidx.constraintlayout:constraintlayout:2.2.0"

也可以去官網查看最新版

使用Motionlayout

方式一:

xml佈局中component Tree > 點擊第一個子項 > 點擊Convert to MotionLayout;

這種方法系統會自動把ConstraintLayout替換成MotionLayout,並且生成MotionScene;MotionScene可以在你的res/xml目錄下找到類似於nihao_scene.xml為名的文件;

詳細介紹:【Android】 MotionLayout詳解_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(運動性描述文件)

  1. 是什麼?
    相應佈局的所有運動描述;也就是説我們定義什麼樣的動畫,就在這個文件裏設置;
  2. 每個 MotionLayout 都引用一個單獨的 MotionScene;那麼怎麼將二者聯繫起來呢?

app:layoutDescription="@xml/motionsence" :通過MotionLayout的 app:layoutDescription聯繫起來;

  1. MotionScene 中的定義的運動描述優先於 MotionLayout 中的任何相同定義;比如定義寬高,最終顯示出來的效果是MotionScene 中定義的寬高;
    MotionLayout其他屬性:
  • showPaths:布爾類型,表示在運動進行時是否顯示運動路徑。默認值為 false;
  • currentState:可指定具體的 ConstraintSet,告訴 MotionLayout 從哪個狀態開始滑動;

其實重要的還是怎麼使用動畫達到我們想要的效果,所以我們來詳細看看MotionScene

一個運動性描述文件框架上主要有Transition,ConstraintSet,Constraint,OnClick,OnSwipe;

簡單分析完成一段動畫需要什麼,我們得確定動畫的剛開始的狀態,結束時的狀態,怎麼觸發這個動畫,一個佈局裏面有很多個view,如何正確的把剛開始的狀態以及結束狀態應用到理想的view上;那麼我們圍繞以上內容進行學習;

ConstraintSet
  1. 介紹: <ConstraintSet> 元素是用來指定視圖在動畫序列中某一點上的位置和屬性(可同時指定多個視圖),也叫約束條件集合;

通俗來講:這就是決定動畫某一時刻(比如起始)的狀態,我們可以在這裏設置很多元素的起始狀態,所以這個就要做約束條件集合;

  1. 代碼模式:
<ConstraintSet android:id = "@+id/end">
  <Constraint
  ...
  </Constraint>
    <Constraint
    ...
    </Constraint>
      </ConstraintSet>
  1. 屬性:
  1. android:id = "@+id/end" 指定這個約束集的名字;
  2. deriveConstraintsFrom:值為另一個 ConstraintSet 的 ID;如果指定這個屬性,ID 對應約束集合內的所有約束條件都將應用於此集合,除非此集明確替換它們;
  1. 裏面必須包含一個或者多個Constraint元素;
Constraint
  1. 介紹:<Constraint> 元素用來聲明運動序列其中一個視圖的位置和屬性,也就是視圖的約束;

通俗來講:規定具體的view的位置以及屬性,為它設置狀態;

  1. 代碼:
<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>
  1. 屬性:
  1. android:layout_height="64dp"這裏設置寬高的優先級高於view自身;

這裏有一個疑問?為什麼在主佈局中定義了view的寬高,為什麼這裏還要再定義一遍?

答案:主佈局設置的是初始值,而這裏設置的是在這個動畫中這個狀態下的view的大小;

  1. android:id = "@+id/TODO"這裏的ID不是為了起名字,而是為了指定作用於哪個view;
CustomAttribute
  1. 介紹:對view變化過程中進行插值,是運行時屬性,比如背景顏色,文字顏色,圓角半徑等;
  2. 代碼:
<CustomAttribute
motion:attributeName="backgroundColor"
motion:customColorValue="#D8C7D5">
</CustomAttribute>
  1. 屬性:
  1. motion:attributeName你要變化的屬性名
  2. motion:customColorValue變化的數值
  1. 常見的屬性表如下:

屬性類型

對應的字段名

示例

説明

顏色

customColorValue

#FF4081

可對顏色進行漸變(自動補間 RGB)

浮點數

customFloatValue

0.5

常用於透明度、圓角半徑、進度等

整數

customIntegerValue

100

如寬高、邊距、進度值等

布爾

customBooleanValue

true

通常配合狀態切換使用

字符串

customStringValue

“Hello”

很少用於動畫,但可以保存狀態

維度

customDimension

16dp

常用於陰影半徑、圓角、邊距等

  1. 如果你要定義,那麼這個view的全程都應該寫CustomAttribute
Transition
  1. 介紹: 該元素是用來聲明運動過程中的開始和結束狀態,包括所有預期的過度狀態、用户的觸發的交互等。
    通俗來講:我們可能會定義很多個狀態,那麼它就是將開始的狀態和結束的狀態連接到一起作為一個動畫,就是Transition 的作用
  2. 代碼:
<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>
  1. 屬性:
  1. motion:constraintSetStart="@+id/start" 確實運動過程中的起始狀態;
  2. motion:constraintSetEnd="@+id/middle" 同理結束狀態;
  3. motion:duration="500" 動畫持續的時間,如果沒寫,默認父元素中設置的時間,所以我們父元素也就是MotionScene中可以設置,屬性是motion:defaultDuration="1000"
  1. Transition 可以包含的元素:OnClick,OnSwipe,KeyFrameSet
OnClick
  1. 介紹: 該元素用於指定當用户點按特定視圖時要執行的操作,指定當用户點擊視圖時觸發動畫序列;

通俗來講:就是點擊觸發動畫;

  1. 代碼
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/middle"
motion:duration="500">
<OnClick
motion:targetId="@+id/TODO"
motion:clickAction="transitionToEnd">
</OnClick>
  </Transition>
  1. 屬性:
  1. motion:targetId="@+id/TODO" 你要滑動觸發的view的對象;
  2. motion:clickAction="transitionToEnd" 代表點擊觸發的操作;
  1. 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定義的起始和結束狀態都是一樣的,那麼設立了兩個點擊觸發,就可以達到點擊到結束狀態,再點擊回到初始狀態;

  1. 為什麼不寫在一個Transition裏面?只能寫一個,寫多了編譯器報錯;

實現的效果如下:

OnSwipe
  1. 介紹: 該元素用於指定當用户在不居中滑動時需要執行的操作;

通俗來講:就是滑動觸發動畫;

  1. 代碼
<OnSwipe
motion:touchAnchorId="@id/page"
motion:touchRegionId="@id/page"
motion:dragDirection="dragLeft"
motion:onTouchUp="autoComplete"
>
</OnSwipe>
  1. 屬性:
  1. motion:touchAnchorId="@id/page" 你的目標view的ID;
  2. motion:touchRegionId="@id/page" 你的錨點區域;
  3. motion:dragDirection="dragLeft" 你朝哪個方向滑動觸發
  4. motion:onTouchUp="autoComplete" 觸摸結束之後的動畫

實現的效果如下:

  1. 其他屬性:
    motion:dragScale:控制目標視圖的滑動距離和用户手指滑動距離的相對比例,默認值是1。通俗來講,取值小於1時view比手滑動的慢,取值大於1時view比手快;

motion:maxVelocity:目標view的最大速度;

motion:maxAcceleration:目標view的最大加速度;

  1. motion:onTouchUp屬性其他可選值:

stop(停止動畫)、autoComplete(自動完成動畫)、autoCompleteToEnd(自動完成到結束狀態)、autoCompleteToStart(自動完成到開始狀態)、decelerate(減速停止動畫)、decelerateAndComplete(減速並完成動畫);

KeyFrameSet
  1. 介紹: <KeyFrameSet> 就是提供聲明更加複雜動畫的元素,他聲明瞭動畫運動軌跡的關鍵點集合,平滑的連接這些關鍵點,可以得到動畫;

通俗來講:它是一個定義關鍵幀的容器,通過關鍵幀,可以精確定義動畫的軌跡和行為;

  1. 代碼
<KeyFrameSet>
  <KeyPosition>
    </KeyPosition>
      <KeyAttribute>
        </KeyAttribute>
          </KeyFrameSet>

它有很多關鍵幀類型,每種類型有不同的屬性,觸發不同的事件,這個關鍵幀通過動畫的時間軸(frameposition)來定位;

關鍵幀`
KeyPosition
  1. 介紹:是一個關鍵幀類型,用於定義視圖在動畫路徑上的位置和大小變化;
  2. 代碼:
<KeyPosition
motion:framePosition="40"
motion:keyPositionType="parentRelative"
motion:motionTarget="@+id/TODO"
motion:percentX="0.45"
motion:percentY="0.75"
>
</KeyPosition>
  1. 屬性:
  1. motion:framePosition="40" 定義了動畫幀的位置,值為0-100,代表在動畫過程中的百分比位置;
  2. motion:motionTarget="@+id/TODO" 目標view
  3. motion:percentX="0.45"取值在-1 - 1之間,座標軸取決於:keyPositionType
  4. motion:keyPositionType="parentRelative" 計算關鍵幀的位置,有三種計算方式:
三種計算方式:

第一種parentRelative:相當於父容器來計算關鍵幀,左上角為(0,0);

``

詳細介紹:【Android】 MotionLayout詳解_關鍵幀_02

第二種:deltaRelative: 基於相對於起始點和結束點的相對偏移量來計算路徑,起始座標和終點座標分別為(0,0)和(1,1)

詳細介紹:【Android】 MotionLayout詳解_xml_03

第三種:以路徑為x軸,以垂直方向為Y軸

詳細介紹:【Android】 MotionLayout詳解_xml_04

KeyAttribute
  1. 介紹: <KeyAttribute> 元素是用來在運動序列的特定時刻,設置視圖的任何標準屬性;
    通俗來講:KeyPosition是規定view的位置和大小,KeyAttribute是規定view的UI元素屬性的關鍵幀類型;可以在動畫的某些時刻對元素的多個屬性進行變換,例如透明度、旋轉、縮放、平移等,從而實現複雜的動畫效果;
  2. 代碼:
<KeyAttribute
motion:motionTarget="@+id/TODO"
motion:framePosition="50"
android:scaleX="1.5"
android:scaleY="1.5"
android:rotation="359"
>
</KeyAttribute>
  1. 屬性:
  1. motion:motionTarget="@+id/TODO" 目標view
  2. motion:framePosition="50" 關鍵幀在動畫進度中的位置
  3. android:scaleX="1.5" 表示將view的weight擴大到1.5倍
  4. android:scaleY="1.5" 同理
  5. android:rotation="359" 將view旋轉359度
  1. 其他屬性:
  1. android:alpha 設置透明度
  2. android:translationXandroid:translationY:控制視圖的 X、Y 方向偏移;
  3. android:transformPivotXandroid:transformPivotY:控制旋轉和縮放的中心點;
  4. transformPivotTarget: 指定其他視圖作為旋轉和縮放的中心點;
  5. motion:transitionEasing="easeInOut" 設置速度
    取值:加速(ease-in)減速(ease-out)先快後慢(easeInOut

有這樣的效果:

詳細介紹:【Android】 MotionLayout詳解_xml_05

代碼如下:

<?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