Android MotionLayout相關(guān)

1、概述

I / O '18提到了MotionLayout,當(dāng)時還沒有正式發(fā)布前段時間,在今年的6月26日正式發(fā)布了ConstraintLayout的2.0alpha版,也算正式推出了MotionLayout。 MotionLayout是ConstraintLayout的子類,它具有ConstraintLayout的所有屬性。MotionLayout用來處理兩個ConstraintSet之間的切換,并在根據(jù)兩個ConstraintSet的CustomAttribute參數(shù)來自動生成切換動畫,關(guān)于ConstraintSet下面會討論。同時MotionLayout所增加的是可以直接通過觸摸屏幕來控制動畫的運(yùn)行進(jìn)度。也就是說MotionLayout會管理你的觸摸事件通過跟蹤手指的速度,并將其與系統(tǒng)中的視圖速度相匹配。從而可以自然地在兩者之間通過觸摸滑動平穩(wěn)過渡。并且在動畫里面加入了關(guān)鍵幀的概念,使得其自動生成動畫在運(yùn)行時某一階段會運(yùn)行到關(guān)鍵幀的狀態(tài)。同時MotionLayout支持在XML中完全描述一個復(fù)雜的動畫,而不需要通過Java代碼來實(shí)現(xiàn)。

ConstraintSet之間切換

2、ConstraintLayout動畫

ConstraintLayout中的動畫要借助于ConstraintSet。ConstraintSet是一個輕量級對象,表示ConstraintLayout中所有子元素的constraints,margins和padding 。當(dāng)將 ConstraintSet應(yīng)用于顯示ConstraintLayout時,布局會使用ConstraintSet中的約束來更新對應(yīng)ConstraintLayout中的。ConstraintSet僅為視圖的大小和位置設(shè)置動畫,不會為其他屬性設(shè)置動畫(例如顏色)。

下面的代碼示例顯示了如何動畫將單個按鈕移動到屏幕底部:


public class MainActivity extends AppCompatActivity {

    ConstraintLayout constraintLayout;
    Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.keyframe_one);
        constraintLayout = findViewById(R.id.constraint_layout);

        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                animateToKeyframeTwo();
            }
        });
    }

    void animateToKeyframeTwo() {

        ConstraintSet constraintSet = new ConstraintSet();
        constraintSet.load(this, R.layout.keyframe_two); //載入要更新的布局到constraintSet中
        TransitionManager.beginDelayedTransition(constraintLayout); // 開啟
        constraintSet.applyTo(constraintLayout);
    }
}


ConstraintSet動畫

這邊要注意一下,只會運(yùn)行R.layout.keyframe_two中與R.layout.keyframe_one中id對應(yīng)的動畫。不會出現(xiàn)R.layout.keyframe_two中有而R.layout.keyframe_one中沒有的視圖,也不會對R.layout.keyframe_one有但R.layout.keyframe_two中沒有的視圖有任何效果。

3、MotionLayout動畫

前面已經(jīng)演示了怎么對ConstraintLayout布局設(shè)置動畫,現(xiàn)在來討論下MotionLayout布局下的動畫。

3.1 MotionScene

與通常的布局不同,MotionLayout所做的約束保存在一個單獨(dú)的XML文件MotionScene中,該文件存儲在您的res/xml目錄中。

MotionScene包含內(nèi)容

MotionScene文件可以包含指定動畫所需的全部內(nèi)容,例如前面提到的ConstraintSets、ConstraintSets直接的過渡、關(guān)鍵幀、觸摸處理等等。

3.2 創(chuàng)建動畫簡單流程

3.2.1 先決條件

① Android Studio 3.2.0或更高版本

② 運(yùn)行Android API等級21或更高版本的設(shè)備或模擬器

③ 添加依賴:


implementation 'com.android.support:appcompat-v7:27.0.2'

implementation 'com.android.support.constraint:constraint-layout:2.0.0-alpha1'

或者


implementation 'androidx.appcompat:appcompat:1.0.0-beta1'

implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha1'

這邊要說明一下androidx也是今年IO剛剛推出的一個依賴,用來替代之前的com.android.support依賴。添加了它就不用添加一大堆v4、v7、v13等依賴了。

3.2.2 定義布局

前面提到過MotionLayout是ConstraintLayout的子類,所以MotionLayout可以直接替換ConstraintLayout。因?yàn)镃onstraintLayout有的功能MotionLayout都有。

3.2.3 創(chuàng)建MotionScene

這一步是MotionLayout的關(guān)鍵,在res下的xml文件夾中創(chuàng)建MotionScene。其實(shí)在MotionLayout中可以不用添加想進(jìn)行動畫的視圖的約束,而將約束放在ConstraintSet中,在將ConstraintSet放在MotionScene中。


<MotionScene

    xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:app="http://schemas.android.com/apk/res-auto">

    <ConstraintSet android:id="@+id/starting_set">

        <Constraint android:id="@+id/actor"

            app:layout_constraintBottom_toBottomOf="parent"

            app:layout_constraintRight_toRightOf="parent"

            android:layout_width="60dp"

            android:layout_height="60dp"/>

    </ConstraintSet >

    <ConstraintSet  android:id="@+id/ending_set" >

        <Constraint android:id="@+id/actor"

            app:layout_constraintTop_toTopOf="parent"

            app:layout_constraintLeft_toLeftOf="parent"

            android:layout_width="60dp"

            android:layout_height="60dp"/>

    </ConstraintSet >

</MotionScene>

這邊需要注意的是每個ConstraintSet 里面的元素必須始終指定所需的位置和所需的大小。它會覆蓋任何以前設(shè)置的布局信息。并且里面的id和MotionLayout中的視圖的id要對應(yīng)才會有反應(yīng)。

為了幫助MotionLayout 的視圖理解必須約束集的順序,需要創(chuàng)建一個Transition 元素。通過使用其直觀命名 constraintSetStart 和constraintSetEnd 屬性,可以指定首先應(yīng)用哪個集合以及最后應(yīng)用哪個集合。該Transition 元素還允許指定動畫的持續(xù)時間。


// 放在上面的<MotionScene> 和</MotionScene>之中和ConstraintSet 標(biāo)簽平級。

<Transition

    android:id="@+id/my_transition"

    app:constraintSetStart="@+id/starting_set"

    app:constraintSetEnd="@+id/ending_set"

    app:duration="2000"/>

此時,一個簡單的MotionScene完成。但是此時任然沒有和MotionLayout進(jìn)行綁定。需要給MotionLayout添加app:layoutDescription屬性來將上面的MotionScene綁定:


app:layoutDescription="@xml/my_scene"

3.2.4 啟動動畫

運(yùn)行應(yīng)用程序時,MotionLayout 視圖將自動將constraintSetStart 屬性中指定的約束集設(shè)置到自己身上。因此,要啟動動畫,需要做的就是調(diào)用transitionToEnd() 方法從而實(shí)現(xiàn)ConstraintSet之間的轉(zhuǎn)換:


motion_container.transitionToEnd();

動畫效果

3.2.5 動畫執(zhí)行進(jìn)度監(jiān)聽

可以通過給MotionLayout 設(shè)置監(jiān)聽器來監(jiān)聽動畫進(jìn)度,和動畫完成時的回調(diào):


motionLayout.setTransitionListener(new MotionLayout.TransitionListener() {

            @Override

            public void onTransitionChange(MotionLayout motionLayout, int i, int i1, float v) {

                seekBar.setProgress((int)(v*100));

            }

            @Override

            public void onTransitionCompleted(MotionLayout motionLayout, int i)             {

            }

});

上面對進(jìn)度的監(jiān)聽通過seekbar表示出來

動畫進(jìn)度監(jiān)聽

3.3 關(guān)鍵幀(Key Frames)

在上面的動畫中,Button小部件看起來像在直線的路徑中移動。這是因?yàn)镸otionLayout 的視圖此時其實(shí)只有兩個關(guān)鍵幀:起始幀Button位于屏幕的右下角,終點(diǎn)幀Button位于屏幕的左上角。如果要改變路徑的形狀,則必須提供一些介于起點(diǎn)和終點(diǎn)之間關(guān)鍵。

在開始創(chuàng)建關(guān)鍵幀之前,必須將KeyFrameSet 標(biāo)簽添加到MotionScene之中??梢宰杂蓜?chuàng)建任意數(shù)量的關(guān)鍵幀。


<KeyFrameSet >

            ...

</KeyFrameSet >

MotionLayout 視圖支持許多不同類型的關(guān)鍵幀。這里使用其中兩種類型:KeyPosition 和KeyCycle 。

3.3.1 KeyPosition

KeyPosition 可以幫助視圖改變運(yùn)動路徑的形狀。創(chuàng)建它們時,請確保提供目標(biāo)視圖的ID,沿時間軸的位置,可以是0到100之間的任意數(shù)字,以及指定X或Y坐標(biāo)已經(jīng)運(yùn)行到的百分比??梢栽O(shè)置type參數(shù)指出坐標(biāo)是相對于實(shí)際的X或Y軸,還是相對于路徑本身。


<KeyFrameSet >

    <KeyPosition

    app:target="@+id/button"

    app:framePosition="30"

    app:type="deltaRelative"

    app:percentX="0.85"/>

    <KeyPosition

    app:target="@+id/button"

    app:framePosition="60"

    app:type="deltaRelative"

    app:percentX="1"/>

</KeyFrameSet>

上面第一個KeyPosition代表button按鈕在運(yùn)行道30%的時候,相對于運(yùn)行軌跡x已經(jīng)運(yùn)行了85%了。第二個KeyPosition代表button按鈕在運(yùn)行道60%的時候,相對于運(yùn)行軌跡x已經(jīng)運(yùn)行了100%了.效果如下,這樣就可以避開和seekbar的沖突了:

KeyPosition關(guān)鍵幀

3.3.2 KeyCycle

KeyCycle用來給動畫添加振動??梢酝ㄟ^提供諸如要使用的波形和波形周期等詳細(xì)信息來配置KeyCycle。下面是KeyCycle支持的各種振動波形:

KeyCycle波形

在上述動畫中加入如下KeyCycle


<KeyCycle

    app:target="@+id/button"

    app:framePosition="30"

    android:rotation="50"

    app:waveShape="sin"

    app:wavePeriod="1"/>

KeyCycle關(guān)鍵幀

3.4 交互式動畫

上面的動畫運(yùn)行我都是通過對Button按鈕設(shè)置點(diǎn)擊監(jiān)聽事件,然后調(diào)用motion_container.transitionToEnd();方法來使他運(yùn)行的。其實(shí)完全不必這么麻煩,因?yàn)镸otionLayout的視圖允許開發(fā)者將觸摸事件直接附加到視圖中。截止到現(xiàn)在,它支持點(diǎn)擊和滑動事件。要實(shí)現(xiàn)上面實(shí)現(xiàn)的點(diǎn)擊事件可以在MotionScene中增加代碼如下:


<OnClick

    app:target="@+id/button"

    app:mode="transitionToEnd"/>

而可以通過給MotionScene增加OnSwipe標(biāo)簽來使視圖通過在屏幕滑動而大運(yùn)行。在創(chuàng)建該標(biāo)簽時,必須確保提供正確的拖動方向以及應(yīng)作為拖動控制柄的視圖的邊??梢赃@么理解,相對于初始位置,如果想往上滑起到增加動畫進(jìn)度就設(shè)置為dragUp,想往下滑起到增加動畫進(jìn)度就設(shè)置為dragDown,左右同樣道理。至于touchAnchorSide這個參數(shù)的本意應(yīng)該設(shè)置拉目標(biāo)視圖的邊,但我發(fā)現(xiàn)就算不設(shè)置touchAnchorSide這個參數(shù)或者設(shè)置成任意值top bottom或者left right,對動畫都沒有影響。這可能是MotionLayout的一個bug畢竟現(xiàn)在還只是alpha版。


<OnSwipe

    app:touchAnchorId="@+id/actor"

    app:dragDirection="dragUp"/>

OnSwipe動畫

5、MotionEditor

之前一篇討論ConstraintLayout的文章,基本上都是在布局編輯器中進(jìn)行操作。這也是ConstraintLayout的一大優(yōu)點(diǎn),MotionLayout作為其子類,官方也為它專門提供了強(qiáng)大的可視化編輯器。不過可惜的是,到目前為止還不能使用,下面是MotionEditor的官方預(yù)告片的一個節(jié)選:

MotionEditor編輯動畫
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 14,041評論 2 59
  • 紫荊花又落了一地,身邊的你又在何方。 昨天下午花花約我去她家,讓我?guī)兔o正在讀高一的燕燕講解數(shù)學(xué)。 花花是我小時候...
    安曉曉閱讀 401評論 0 2
  • 起風(fēng)了,唯有努力試著生存。 from「海濱墓園」 一整天都在外頭跑,老人餃子會實(shí)在不想說。午後糾結(jié)了一下還是決定坐...
    阿飛閱讀 245評論 0 1
  • 2018年3月3日 星期六 天氣:晴 晚上朋友來電話,說一起出去吃飯。我沒有推遲,叫上老婆孩子就去了。 我這個朋友...
    嗚嗚嗚嗚嗚嗚嗚嗚閱讀 161評論 0 0
  • 原創(chuàng):王漫 左宗棠,湖南湘陰人,晚清中興儒將,精通韜略,足智多謀。其一生對中華民族,甚至對自己,最大貢獻(xiàn)就是率軍抵...
    武商路漫漫閱讀 2,744評論 49 28

友情鏈接更多精彩內(nèi)容