學如逆水行舟,不進則退。
接觸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,待界面顯示出來後,點選傳回鍵,退出。效果如下圖
正如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
此過程的過渡效果如下
可以看到,正如上述結論,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類型);第三個參數為過渡名稱。共享元素須設定相同的名稱。
無需另加效果,以上調用即可實作共享元素過渡。
是不是感覺很酷炫?
需要注意的是,這裡的兩個文本元素的控件屬性是相同的(字型大小、顔色等),如果不同,原生的效果暫不支援,我們就會看到過渡動畫過程中出現突變的問題。這個需要自定義實作,暫時留到以後再作讨論。
(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"/>
兩個界面中,圖檔的位置和大小,都有差别。
成功實作圖檔過渡,并且還有圖檔尺寸的變化。
不知道細心的你有沒有發現,前面的兩個過渡圖檔控件,并沒有同時指定過渡名稱(transitionName),但是依然成功的實作了過渡效果。經實驗發現,隻要過渡目标控件有指定過渡名稱,就沒有問題了。至于為什麼,暫不清楚。
(3) 共享元素組過渡
如果我們需要将多個不同的共享元素同時進行過渡,也是允許的,隻需要傳入Pair
options = ActivityOptions.makeSceneTransitionAnimation(this,
new Pair<View, String>(mImage, "test_image"), new Pair<View, String>(mText, "test_text"));
小結
Android動畫系統包含了太多有趣且實用的功能,但鑒于目前廣大産品設計者和開發們沒有深入挖掘,白白浪費掉了現成的好東西。Activity過渡就是其中一個。鄙人在此隻作了一個簡單的學習總結,抛誇引玉。如有不當和謬誤之處,煩請各位看官不吝賜教啊。
附上源碼,僅供參考。