天天看點

Android動畫 —— Activity過渡類别過渡動畫小結

學如逆水行舟,不進則退。

接觸Android開發雖已經頗有時日,但感覺相關知識總停留于一知半解,或者又缺乏系統關聯導緻顧此而失彼。是時候總結一下了。

那就從常常開發過程中經常遇到的Android動畫開始吧。

Material設計為Android增加了一種動畫稱為Activity過渡(Activity Transitions),旨在提供不同狀态下共有元素的視覺連接配接。官方文檔的描述,聽起來拗口,讀起來也不夠順暢。Activity過渡在我接觸的項目中沒有遇到過,今天就以它初試牛刀,既是學習,也是鞏固。

類别

Activity的過渡動畫總共可以分為三類:

  • 進入過渡(Enter transition):定義Activity中的view如何進入界面
  • 退出過渡(Exit transition):定義Activity中的view如何退出界面
  • 共享元素過渡(Shared elements transition): 如果兩個Activity包含了若幹“共享元素”(可以了解為相同的元素控件,至少,看起來是如此),那麼共享元素過渡動畫,就是将這些共享元素從一個Activity優雅地過渡到另一個Activity

任何繼承了Visibility類的過渡,都可以作為進入或者退出過渡。目前,Android預設支援的進入/退出過渡如下表。

過渡 說明
explode 移動view進入/退出界面中央
slide 從界面的某一條邊移入/移出view
fade 通過改變透明度來展現view的添加和移出

Android預設支援的共享元素過渡如下表。

過渡 說明
changeBounds 過渡view的layout bound變化
changeClipBounds 過渡view的clip bound變化
changeTransform 過渡view的scale和rotation的變化
changeImageTransform 過渡image的尺寸和放縮比例變化

過渡動畫

進入/退出過渡

首先,開啟過渡動畫需要在style中将windowActivityTransitions屬性設定為true

或者,也可以在代碼中調用方法來設定

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
           

前面已經說過,進入、退出過渡,是指Activity中的view元素,在Activity界面顯示時的動畫過渡。下面,我們使用預設的Explode過渡來舉個例子。

(1) SecondActivity

定義SecondActivity,用于顯示進入和退出過渡,界面為一個TextView和四個定位View(用處後面會說),代碼如下

<?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.djx.activitytransition.SecondActivity">

        <View
            android:layout_width="16dp"
            android:layout_height="16dp"
            android:layout_margin="64dp"
            android:layout_gravity="top"
            android:background="#888" />

        <View
            android:layout_width="16dp"
            android:layout_height="16dp"
            android:layout_margin="64dp"
            android:layout_gravity="top|right"
            android:background="#888" />

        <View
            android:layout_width="16dp"
            android:layout_height="16dp"
            android:layout_margin="64dp"
            android:layout_gravity="bottom"
            android:background="#888" />

        <View
            android:layout_width="16dp"
            android:layout_height="16dp"
            android:layout_margin="64dp"
            android:layout_gravity="bottom|right"
            android:background="#888" />

        <TextView
            android:id="@+id/hello"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:transitionName="test_text"
            android:textSize="45sp"
            android:text="Hello"/>

    </FrameLayout>
           

同時,在onCreate中設定并開啟進入過渡,并使用内置的Explode效果。

getWindow().setEnterTransition(new Explode());
           

(2) MainActivity

主界面Activity,用于啟動SecondActivity,并添加transition的選項bundle

Intent intent = new Intent(this, SecondActivity.class);
    ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this);
    startActivity(intent, options != null ? options.toBundle() : null);
           

(3) 過渡效果

啟動SecondActivity,待界面顯示出來後,點選傳回鍵,退出。效果如下圖

Android動畫 —— Activity過渡類别過渡動畫小結

正如Explode的描述,進入時,界面中的所有View都是從外部往中間飛入(注意方向是中心);而退出時,所有View都是以中心為源而向外飛散。上面定義的文本及四個不同位置的定位View,就是為了讓這個現象更清楚。

例子中都是使用代碼方式控制了過渡,其實,更加簡單地,可以直接在style裡面設定windowEnterTransition和windowExitTransition兩個屬性來實作。這裡就不贅述了。

值得注意的是,啟動一個Activity,或者是退出覆寫其上的其他Activity,使其切回前台,都是一個“進入”過程,都會執行進入過渡;同樣地,退出一個Activity,或者在其界面啟動其他Activity導緻其退到背景,都是一個“退出”過程,也都會執行退出過渡。而且,這兩組進入與退出,是互相獨立互不幹擾的。

為了證明上面結論,增加一個ThirdActivity,并在SecondActivity中添加一個按鍵用于啟動它。同時,為了進行區分,SecondActivity啟動ThirdActivity的退出過渡,我們設定為另一個預設效果Slide,方向為向左。

getWindow().setExitTransition(new Slide(Gravity.LEFT));
    startActivity(new Intent(SecondActivity.this, ThirdActivity.class),
      ActivityOptions.makeSceneTransitionAnimation(SecondActivity.this).toBundle());
           

啟動和退出Activity順序為

1.MainActivity - 2.SecondActivity - 3.ThirdActivity - 4.SecondActivity - 5.MainActivity

此過程的過渡效果如下

Android動畫 —— Activity過渡類别過渡動畫小結

可以看到,正如上述結論,SecondActivity被啟動時,采用Explode效果進入,在最後退出時,也會是此效果退出(即2的進入、4的退出為一組Explode);當SecondActivity啟動ThirdActivity時,退出到背景,執行了Slide效果,退出ThirdActivity使其重新回到前台時,同樣執行了Slide效果進入(即2的退出、4的進入為一組Slide)。SecondActivity在整個過程中有兩組進入與退出,且互相獨立。

共享元素過渡

在兩個Activity中,如果有兩個或者多個元素控件相同(至少外表看起來如此),那麼,我們就稱這兩個或者多個控件是共享元素。還是用例子來說明比較清晰一點。

在SecondActivity中,增加一個TextView,文本内容是“World”

<TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="World"
        android:textSize="32sp"
        android:layout_gravity="bottom|left"
        android:layout_marginLeft="16dp"
        android:layout_marginBottom="16dp"
        android:transitionName="test_text" />
           

同樣,在ThirdActivity中,也增加一個TextView,文本内容也是“World”

<TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="World"
        android:textSize="32sp"
        android:layout_gravity="center"
        android:transitionName="test_text" />
           

這樣一來,兩個分屬不同Activity的文本控件,内容相同,那麼我們就認為這兩個控件是共享元素。現在我們要做的,就是讓這兩個Activity互相跳轉的時候,共享元素能夠神奇地從一個Activity過渡到另一個。

(1) 文本過渡

以上面的兩個TextView作過渡,十分簡單

startActivity(new Intent(this, ThirdActivity.class),
      ActivityOptions.makeSceneTransitionAnimation(this, mText, "test_text").toBundle());
           

與進入/退出過渡類似,也調用了makeSceneTransitionAnimation方法,不過此處參數略有不同:第二個參數指定共享元素(View類型);第三個參數為過渡名稱。共享元素須設定相同的名稱。

無需另加效果,以上調用即可實作共享元素過渡。

Android動畫 —— Activity過渡類别過渡動畫小結

是不是感覺很酷炫?

需要注意的是,這裡的兩個文本元素的控件屬性是相同的(字型大小、顔色等),如果不同,原生的效果暫不支援,我們就會看到過渡動畫過程中出現突變的問題。這個需要自定義實作,暫時留到以後再作讨論。

(2) 圖檔過渡

圖檔的共享過渡,比起文本來說,原生實作得更好,不同大小的共享圖檔也可以完美過渡。

類似地,分别在SecondActivity和ThirdActivity中增加一個共享ImageView。

SecondActivity中的圖檔

<ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="16dp"
        android:layout_gravity="bottom|right"
        android:src="@mipmap/ic_launcher"/>
           

ThirdActivity中的圖檔

<ImageView
        android:id="@+id/image"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:layout_gravity="bottom"
        android:layout_marginLeft="80dp"
        android:layout_marginBottom="80dp"
        android:scaleType="centerCrop"
        android:transitionName="test_image"
        android:src="@mipmap/ic_launcher"/>
           

兩個界面中,圖檔的位置和大小,都有差别。

Android動畫 —— Activity過渡類别過渡動畫小結

成功實作圖檔過渡,并且還有圖檔尺寸的變化。

不知道細心的你有沒有發現,前面的兩個過渡圖檔控件,并沒有同時指定過渡名稱(transitionName),但是依然成功的實作了過渡效果。經實驗發現,隻要過渡目标控件有指定過渡名稱,就沒有問題了。至于為什麼,暫不清楚。

(3) 共享元素組過渡

如果我們需要将多個不同的共享元素同時進行過渡,也是允許的,隻需要傳入Pair

options = ActivityOptions.makeSceneTransitionAnimation(this,
                  new Pair<View, String>(mImage, "test_image"), new Pair<View, String>(mText, "test_text"));
           
Android動畫 —— Activity過渡類别過渡動畫小結

小結

Android動畫系統包含了太多有趣且實用的功能,但鑒于目前廣大産品設計者和開發們沒有深入挖掘,白白浪費掉了現成的好東西。Activity過渡就是其中一個。鄙人在此隻作了一個簡單的學習總結,抛誇引玉。如有不當和謬誤之處,煩請各位看官不吝賜教啊。

附上源碼,僅供參考。

繼續閱讀