「譯」 MotionLayout 介紹 (Part IV) 深入理解關(guān)鍵幀

原文鏈接

在 MotionLayout 中定義運(yùn)動(dòng)路徑

介紹

MotionLayout 是一個(gè)來(lái)自 ConstraintLayout 2.0 的專(zhuān)注于動(dòng)畫(huà)的新布局。本系列的前幾篇文章對(duì)該系統(tǒng)進(jìn)行了很好的概述。我強(qiáng)烈建議你在閱讀本文前先去查看它們。

MotionLayout 動(dòng)畫(huà)系統(tǒng)通過(guò)在兩種狀態(tài)之間插入值(通常是控件的位置/大小)來(lái)工作,這些值是使用 ConstraintLayout 的約束系統(tǒng) (ConstranitSets) 以及視圖屬性來(lái)指定的。這兩種狀態(tài)之間的轉(zhuǎn)換也可以完全由觸摸事件驅(qū)動(dòng)。這個(gè)系統(tǒng)通常會(huì)為你的過(guò)渡提供很好的效果。

除了上面說(shuō)的狀態(tài)之外,MotionLayout 還支持關(guān)鍵幀(在本系列的第二部分中簡(jiǎn)單介紹過(guò)),我們將在本文中深入介紹這些關(guān)鍵幀。注意,雖然關(guān)鍵幀很好,但是它絕對(duì)是一個(gè)更專(zhuān)業(yè)的工具;你可能不需要或者偶爾才會(huì)用到。

請(qǐng)記住,在應(yīng)用中添加的動(dòng)畫(huà)應(yīng)該有它的意義;不要濫用!

但是,如果需要對(duì)你的過(guò)渡效果添加額外的功能,那么關(guān)鍵幀可以幫助你擴(kuò)展 MotionLayout 的功能。如你所見(jiàn),這里有很多內(nèi)容需要覆蓋:

上手關(guān)鍵幀(a Rendez-vous in Time)

從較高的層次上看,關(guān)鍵幀可以對(duì)你的兩個(gè)狀態(tài)之間的插值進(jìn)行一個(gè)修改。

Keyframes-1

MotionLayout 支持不同的關(guān)鍵幀:

  • 位置關(guān)鍵幀 Position keyframe : KeyPosition
  • 屬性關(guān)鍵幀 Attribute keyframe : KeyAttribute
  • 循環(huán)關(guān)鍵幀 Cycle keyframe : KeyCycle
  • 周期關(guān)鍵整 TimeCycle keyframe : KeyTimeCycle

注意,每種類(lèi)型的關(guān)鍵幀都是獨(dú)立于其他類(lèi)型的關(guān)鍵幀的——也就是說(shuō),你不需要在相同的點(diǎn)上定義所有的關(guān)鍵幀(但是你不能在相同的點(diǎn)上定義相同類(lèi)型的多個(gè)關(guān)鍵幀)

通用屬性

所有關(guān)鍵幀(位置、屬性、循環(huán)、周期)都有一些關(guān)鍵的通用屬性:

  • 節(jié)點(diǎn) motion:framePosition : 關(guān)鍵幀在過(guò)渡中(從0到100)的作用時(shí)機(jī)
  • 目標(biāo) motion:target : 哪個(gè)對(duì)象受該關(guān)鍵幀影響
  • 插值器 motion:transitionEasing : 使用哪種插值器(默認(rèn)為線性)
  • 曲線擬合motion:curveFit : 樣條(默認(rèn))或線形——使用哪個(gè)曲線擬合關(guān)鍵幀。默認(rèn)情況下是單調(diào)樣條曲線,這使得過(guò)渡更加平滑,當(dāng)然你也可以決定使用線性 (linear) 擬合。

位置關(guān)鍵幀

位置關(guān)鍵幀可能是你最常使用到通用關(guān)鍵幀。它允許你修改控件在過(guò)渡期間在屏幕上的路徑。舉例,讓我們?cè)?MotionLayout 中為其中的一個(gè)控件做動(dòng)畫(huà):

position-keyframes

我們有一個(gè)起始狀態(tài)(左下)和結(jié)束狀態(tài)(右上),過(guò)渡過(guò)程就是控件在這兩種狀態(tài)之間的線性 (linear interpoltion) 直線運(yùn)動(dòng)。

position-keyframes

通過(guò)引入位置關(guān)鍵幀,我們可以將運(yùn)動(dòng)路徑變成曲線運(yùn)動(dòng):

position-keyframes
position-keyframes

添加更多的關(guān)鍵幀允許你創(chuàng)建復(fù)雜的運(yùn)動(dòng)路徑。

position-keyframes
<KeyFrameSet>
    <KeyPosition
        motion:keyPositionType="pathRelative"
        motion:percentX="0.75"
        motion:percentY="-0.3"
        motion:framePosition="25"
        motion:target="@id/button"/>
    <KeyPosition
        motion:keyPositionType="pathRelative"
        motion:percentY="-0.4"
        motion:framePosition="50"
        motion:target="@id/button"/>
    <KeyPosition
        motion:keyPositionType="pathRelative"
        motion:percentX="0.25"
        motion:percentY="-0.3"
        motion:framePosition="75"
        motion:target="@id/button"/>
</KeyFrameSet>

什么是位置關(guān)鍵幀?

如果 ConstraintSets 已經(jīng)允許你以非常靈活的方式擺放控件,那么你也許會(huì)問(wèn)自己定位關(guān)鍵幀的意義是什么。原因如下:

  • 關(guān)鍵幀表示臨時(shí)修改,而 ConstraintSets 表示“靜止 (resting) ”狀態(tài)
  • 關(guān)鍵幀在計(jì)算中相對(duì)于 ConstraintSet 更加輕量級(jí)
  • 位置關(guān)鍵幀允許你對(duì)一個(gè)控件的運(yùn)動(dòng)路徑進(jìn)行操縱 —— ConstraintSets 則是指定一個(gè)控件相對(duì)與其他控件的位置。

注意:在一個(gè) MotionScene 中定義多個(gè) ConstraintSets 是有可能的,所以如果你有一個(gè)多步驟的動(dòng)作,其中這些步驟是有效的“靜止”狀態(tài),那么你可以使用它們而不是關(guān)鍵幀。狀態(tài)到狀態(tài)的轉(zhuǎn)換必須在代碼中完成(可以使用改動(dòng)監(jiān)聽(tīng)器(change listeners))。

使用 XML 表示

關(guān)鍵幀存在于 <KeyFrameSet> 屬性中,<KeyFrameSet> 則存在于 MotionScene 文件中的 <Transition>,并且至少包含:

  • target: 被關(guān)鍵幀影響的控件
  • framePosition: 關(guān)鍵幀使用時(shí)機(jī),(0-100)
  • keyPositionType: 所使用的坐標(biāo)系 相對(duì)父容器(parentRelative), 三角定位(deltaRelative), 相對(duì)路徑(pathRelative)
  • percentX / percentY :位置的 (x,y) 坐標(biāo)
<Transition ...>
    <KeyFrameSet>
        <KeyPosition
            motion:keyPositionType="parentRelative"
            motion:percentY="0.25"
            motion:framePosition="50"
            motion:target="@+id/button"/>
    </KeyFrameSet>
</Transition>

不同的坐標(biāo)系

在 MotionLayout 中的起始狀態(tài)和結(jié)束狀態(tài)允許復(fù)雜的定位。對(duì)于 ConstraintSets,它們可以使用 ConstraintLayout 的所有功能。系統(tǒng)將根據(jù)密度 (density) 、屏幕方向(screen orientation)、語(yǔ)言(language) 等變化,正確的處理這些狀態(tài)。

要使關(guān)鍵幀在這樣的系統(tǒng)中發(fā)揮作用,我們需要它們能夠以類(lèi)似自適應(yīng)的方式進(jìn)行布局——而不是簡(jiǎn)單的使用固定的位置。

為了解決這個(gè)問(wèn)題,同時(shí)保持關(guān)鍵幀系統(tǒng)的輕量級(jí),我們提出了一種靈活的方法——在給定的坐標(biāo)系中,每個(gè)關(guān)鍵幀的位置用(x,y)坐標(biāo)對(duì) (pair) 表示:

  • motion:percentX=”<float>”
  • motion:percentY=”<float>”

這個(gè)坐標(biāo)的含義取決于所使用的坐標(biāo)系類(lèi)型:parentRelative, deltaRelative, or pathRelative.

注意:每個(gè)關(guān)鍵幀的位置都是單獨(dú)存在——每個(gè)關(guān)鍵幀的位置都可以用它們自己相對(duì)的坐標(biāo)系表示。

相對(duì)父容器(parentRelative)

坐標(biāo)是根據(jù)相對(duì)父容器表示的。這是一種非常直接和直觀的方式來(lái)表達(dá)關(guān)鍵幀的位置,通常就足夠了。通常情況下,你用它來(lái)做與父容器相關(guān)的大范圍運(yùn)動(dòng)。

parentRelative-1

由于這個(gè)坐標(biāo)系只基于父容器維度,而不是移動(dòng)的控件的開(kāi)始/結(jié)束位置,您可能會(huì)遇到這樣的情況,即最后的關(guān)鍵幀位置以次優(yōu)位置結(jié)束(相對(duì)于開(kāi)始/結(jié)束位置)。(原文: you may encounter situations where the resulting keyframe position ends in a suboptimal position (relative to the start/end positions).)

三角定位(deltaRelative)

第二個(gè)坐標(biāo)系通過(guò)使用開(kāi)始/結(jié)束位置定義來(lái)解決這個(gè)問(wèn)題。坐標(biāo)表示起點(diǎn)和終點(diǎn)之間的百分比。

delta-relative-1

和相對(duì)父容器坐標(biāo)系有點(diǎn)像,這是一個(gè)相對(duì)直觀的坐標(biāo)系統(tǒng),一般也會(huì)給出很好的結(jié)果。當(dāng)你希望控件以水平或垂直運(yùn)動(dòng)開(kāi)始或結(jié)束時(shí),它十分有用。

它有一個(gè)潛在的問(wèn)題——因?yàn)樗歉鶕?jù)控件從開(kāi)始到結(jié)束位置之間的差異定義的額,如果差異非常小(或者沒(méi)有)關(guān)鍵幀將不會(huì)在對(duì)應(yīng)軸上發(fā)生變化。例如,如果控件在屏幕從左向右移動(dòng),而保持在相同的高度,那么對(duì)位置關(guān)鍵幀使用 deltarelative percentY 將不會(huì)產(chǎn)生任何效果。

相對(duì)路徑(pathRelative)

最后一個(gè)坐標(biāo)系定義了一個(gè)相對(duì)于從開(kāi)始狀態(tài)到結(jié)束狀態(tài)的直線路徑。它可以解決 deltaRelative 坐標(biāo)系中的問(wèn)題——當(dāng)一個(gè)控件沒(méi)有在垂直軸移動(dòng)的情況下,使用 pathRelative 將允許將位置關(guān)鍵幀設(shè)置為偏離路徑。注意,它也支持負(fù)坐標(biāo)。它是一個(gè)更特殊的坐標(biāo)系,但是在處理時(shí)間上特別有用。下面有一個(gè)例子是實(shí)現(xiàn)一個(gè)曲線形狀(比如“S”形),即使端點(diǎn)發(fā)生變化,它也會(huì)保持不變。

path-relative-1

Arc Motion

在 Material Design 中使用的一種典型的運(yùn)動(dòng)類(lèi)型是圓弧運(yùn)動(dòng)(arc motion)。使用 MotionLayout 創(chuàng)建圓弧運(yùn)動(dòng)的一種方法是在起始位置和結(jié)束位置之間添加正確放置的位置關(guān)鍵幀,如前一節(jié)所述。

在 ConstraintLayout 2.0.0 alpha 2 中,我們引入了一種實(shí)現(xiàn)完美圓弧運(yùn)動(dòng)的新方案——而且它更加容易使用。你只需要將motion:pathMotionArc 屬性添加到起始的 ConstraintSet ,從而讓默認(rèn)的線形運(yùn)動(dòng) (linear motion) 切換到弧線運(yùn)動(dòng) (arc motion) 。

讓我們來(lái)看一個(gè)簡(jiǎn)單的例子,開(kāi)始狀態(tài)是屏幕的右下,結(jié)束的位置是屏幕的頂部并且水平居中。添加下面這個(gè)屬性就可以產(chǎn)生弧線運(yùn)動(dòng):

motion:pathMotionArc=”startHorizontal”

Arc Motion: startHorizontal (preview in Android Studio)

如果把屬性換成:

motion:pathMotionArc=”startVertical”

就會(huì)改變弧線的方向:

Arc Motion: startVertical (preview in Android Studio)

你仍然可以使用位置關(guān)鍵幀來(lái)創(chuàng)造更復(fù)雜的弧線路徑。下面是結(jié)果:

Arc Motion: intermediate keyframe (preview in Android Studio)

它是通過(guò)在動(dòng)畫(huà)中添加一個(gè)垂直居中的位置關(guān)鍵幀來(lái)實(shí)現(xiàn)的:

<KeyPosition
    motion:keyPositionType="parentRelative"
    motion:percentY="0.5"
    motion:framePosition="50"
    motion:target="@id/button"/>

通過(guò)設(shè)置 motion:pathMotionArc 屬性,還可以在該場(chǎng)景中使用關(guān)鍵幀來(lái)更改圓弧的方向。屬性可以是flip (翻轉(zhuǎn)當(dāng)前的圓弧方向)、none (還原為線性運(yùn)動(dòng)),也可以是startHorizontalstartVertical

Arc Motion: intermediate keyframe with flipped direction (preview in Android Studio)
<KeyPosition
    motion:keyPositionType="parentRelative"
    motion:pathMotionArc="flip"
    motion:percentY="0.5"
    motion:framePosition="50"
    motion:target="@id/button"/>
Arc Motion: keyframe with pathMotionArc="none" (preview in Android Studio)
<KeyPosition
    motion:keyPositionType="parentRelative"
    motion:pathMotionArc="none"
    motion:percentY="0.5"
    motion:framePosition="50"
    motion:target="@id/button"/>

時(shí)間模型(Easing)

在前幾節(jié)中,我們介紹了各種機(jī)制幫助你定義一個(gè)運(yùn)動(dòng)路徑。對(duì)于一個(gè)動(dòng)畫(huà),不僅僅需要選擇合適的路徑;時(shí)間也是至關(guān)重要的。

由于位置關(guān)鍵幀可以由時(shí)間指定,你可以使用它們來(lái)定義控件移動(dòng)的快慢,具體取決于移動(dòng)的空間。

但是在一個(gè)單獨(dú)片段內(nèi)——開(kāi)始和結(jié)束狀態(tài)之間,或者在關(guān)鍵幀之間——時(shí)間插值器是線形的。(the time interpolation is linear)

你可以使用motion:transitionEasing 屬性來(lái)指定一個(gè)緩和曲線來(lái)修改它。你可以將這個(gè)屬性使用在ConstraintSets 或 關(guān)鍵幀,它接受這些值:

  • cubic(float, float , float, float), x1,y1,x2,y2 表示一個(gè)從 0,0 到 1,1 的三次貝塞爾方程的控制點(diǎn)
  • 或使用關(guān)鍵字: standard, accelerate, decelerate, 預(yù)定義的曲線類(lèi)似 Material Design definitions.

標(biāo)準(zhǔn)曲線(Standard easing)

corresponding to cubic(0.4, 0.0, 0.2, 1)

通常用于在非觸摸驅(qū)動(dòng)的動(dòng)畫(huà)中。它最適合于開(kāi)始和結(jié)束都是靜止?fàn)顟B(tài)的元素。

加速曲線(Accelerate easing)

corresponding to cubic(0.4, 0.0, 1, 1)

加速通常用于一個(gè)元素移出屏幕。

減速曲線(Decelerate easing)

corresponding to cubic(0.0, 0.0, 0.2, 1)

減速通常用于一個(gè)元素進(jìn)入屏幕。

鍵屬性(KeyAttribute)

屬性關(guān)鍵幀允許你在動(dòng)畫(huà)過(guò)程中指定控件的屬性在給定時(shí)間點(diǎn)的更改——換句話(huà)說(shuō),它們與位置關(guān)鍵幀類(lèi)似,但作用于屬性而不是位置。

key-attribute-1

上面的例子通過(guò)在 MotionScene 文件中添加 KeyAttribute 元素來(lái)實(shí)現(xiàn):

<KeyFrameSet>
    <KeyAttribute
        android:scaleX="2"
        android:scaleY="2"
        android:rotation="-45"
        motion:framePosition="50"
        motion:target="@id/button" />
</KeyFrameSet>

相比于KeyPostion,我們需要指定framePosition(關(guān)鍵幀應(yīng)用時(shí)機(jī))和目標(biāo)(哪個(gè)對(duì)象受到影響)。

支持的屬性

一些你開(kāi)箱即用的 View 屬性:

android:visibility, android:alpha, android:elevation, android:rotation, android:rotationX, android:rotationY, android:scaleX, android:scaleY, android:translationX, android:translationY, android:translationZ

重點(diǎn)

受到應(yīng)用程序的 SDK level 限制,其中一些屬性將不起作用:

  • SDK 21 引入的 android:elevation
  • SDK 21 引入的android:translationZ

自定義屬性(Custom Attributes)

你可以通過(guò)添加 <CustionAttribute> 子節(jié)點(diǎn)在 ConstraintSets 和 KeyAttribute 節(jié)點(diǎn)中聲明自定義屬性。這個(gè)節(jié)點(diǎn)需要一個(gè)屬性名(attributeName),它是getter/setter的名稱(chēng)(除去set/get前綴)和要傳入或使用的值類(lèi)型,屬性類(lèi)型指定為下方其中一個(gè):

  • customColorValue : 顏色值
  • customColorDrawableValue : 顏色值 Drawable
  • customIntegerValue : Integer
  • customFloatValue : Float
  • customStringValue : String
  • customDimension : 尺寸
  • customBoolean : Boolean

舉例,這面是對(duì)應(yīng)上面動(dòng)畫(huà)的 XML:

<ConstraintSet android:id="@+id/start">
    <Constraint
        android:id="@+id/button" ...>
        <CustomAttribute
            motion:attributeName="backgroundColor"
            motion:customColorValue="#D81B60"/>
    </Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
    <Constraint
        android:id="@+id/button" ...>
        <CustomAttribute
            motion:attributeName="backgroundColor"
            motion:customColorValue="#9999FF"/>
    </Constraint>
</ConstraintSet>

總結(jié)

本文介紹了 MotionLayout 中 最常見(jiàn)的關(guān)鍵幀和路徑規(guī)范。我們將在本系列的第五部分討論 循環(huán)(KeyCycle)和 周期(KeyTimeCycle),它們介紹了一種非常強(qiáng)大的方法,可以將擾動(dòng)(類(lèi)似波形)添加到屬性(基于路徑或基于時(shí)間),允許各種有趣但可預(yù)測(cè)的循環(huán)效果(反彈(bounce)、抖動(dòng)(shaking)、脈動(dòng)(pulsations)等)。

使用 MotionLayout 的各種示例可以在 ConstraintLayout examples github repository查看。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • ¥開(kāi)啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開(kāi)一個(gè)線程,因...
    小菜c閱讀 6,547評(píng)論 0 17
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,251評(píng)論 4 61
  • 天氣在這個(gè)北方的小城也逐漸轉(zhuǎn)暖了,下午時(shí)分黑云壓低了空氣,也壓低了這幾日的情緒,山雨欲來(lái)風(fēng)滿(mǎn)樓,再合適不過(guò)了。工作...
    LunarCoronar閱讀 302評(píng)論 0 0
  • 我認(rèn)為上大學(xué)的一個(gè)重要使命是更加細(xì)致地了解與認(rèn)識(shí)自己,熟知自己的性格為人,以便以后能清楚地辨別自己能做什么不能做什...
    默猷閱讀 355評(píng)論 0 2
  • 胡足額閱讀 458評(píng)論 8 7