在今年的 IO 大會上,發(fā)布了一套叫 Android Jetpack 的程序庫。Android Jetpack 里的組件大部分我們都接觸過了,其中也有一些全新的組件,其中一個就是 Navigation。
簡介
Navigation 是用來管理 APP 里頁面跳轉的。起初,我以為它是用來代替 startActivity 的,但其實并不是,大家往下看就知道它的作用了。
另外,iOS 的同學可能會有似曾相識的感覺,Navigation 應該是有借鑒 Storyboard 的。
使用
我們先來看看 Navigation 的實現(xiàn)過程。
添加依賴
首先,需要使用 Android Studio 3.2 以上版本才能使用 Navigation。
在 build.gradle 中添加依賴:
implementation "android.arch.navigation:navigation-fragment:$nav_version"
implementation "android.arch.navigation:navigation-ui:$nav_version"
創(chuàng)建 navigation xml 文件
使用 「Android Resource File」創(chuàng)建 xml 文件的時候,可以看到在類型里,多了一個 Navigation 的選項:
創(chuàng)建成功后,就來到了文章開頭的那個一個可視化的操作界面。點擊左上角的添加小圖標,會出現(xiàn) Activity 和 Fragment,我們這里添加兩個 Activity 和兩個 Fragment:
配置 Action
Fragment 的右邊有個小圓圈,點擊并拖到另一個頁面,這樣我們就給這個 Fragment 添加了一個跳轉行為,也就是 Action。
但是可以發(fā)現(xiàn),Activity 的右邊是沒有這個小圓圈的,所以 Navigation 并不能處理從 Activity 發(fā)起的跳轉。
左上角有個小房子的是顯示的第一個頁面,但由于 Activity 無法發(fā)起跳轉,所以這里把 MainActivity 刪除,把 MainFragment 作為主頁面,并給它添加跳轉到 SecondFragment 和 SecondActivity 的 Action:
自動生成的 xml 代碼是這樣的:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
app:startDestination="@id/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="com.example.navigation.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main">
<action
android:id="@+id/action_mainFragment_to_secondFragment"
app:destination="@id/secondFragment"
app:enterAnim="@anim/slide_in_right" />
<action
android:id="@+id/action_mainFragment_to_secondActivity"
app:destination="@id/secondActivity" />
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name="com.example.navigation.SecondFragment"
android:label="fragment_second"
tools:layout="@layout/fragment_second" />
<activity
android:id="@+id/secondActivity"
android:name="com.example.navigation.SecondActivity"
android:label="activity_second"
tools:layout="@layout/activity_second" />
</navigation>
布局中添加 Fragment
現(xiàn)在,我們第一個頁面是 MainFragment,而 Fragment 需要 Activity 作為容器,修改 MainActivity 的布局:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav" />
</FrameLayout>
其中有三個屬性需要注意。使用 android:name 指定 Fragment 的類型為 NavHostFragment,使用 app:navGraph 指定 Navigation 文件。app:defaultNavHost="true" 的作用是,讓 Navigation 處理返回事件,點返回按鈕時并不是返回上一個 Activity,而是返回上一個「頁面」,上一個「頁面」有可能是 Activity,也可能是 Fragment。
至此,Navigation 的簡單配置就算完成了,接下來看如何使用它。
配置跳轉
在 Navigation 里,頁面的跳轉是交給 NavController 來處理的,獲取 NavController 的方法有這么三種:
NavHostFragment.findNavController(Fragment)
Navigation.findNavController(Activity, @IdRes int viewId)
Navigation.findNavController(View)
拿到后,通過 navigate 方法,通過傳入 Action 的 id,實現(xiàn)跳轉,比如:
NavHostFragment
.findNavController(this)
.navigate(R.id.action_firstFragment_to_secondFragment)
在簡單配置了兩個跳轉后,看一下目前的效果:
傳參
頁面的跳轉少不了數(shù)據(jù)的傳遞,使用 Navigation,和我們原來的跳轉一樣,可以通過 Bundle 來傳遞參數(shù):
val bundle = Bundle()
bundle.putString("name", "SouthernBox")
NavHostFragment
.findNavController(this)
.navigate(R.id.action_firstFragment_to_secondFragment, bundle)
如果跳轉到 Activity,可以從 intent.extras 獲取到 bundle,如果是 Fragment,則從 arguments 獲取到。
此外,還可以在 Navigation 的 xml 文件中配置傳參,但這種方式目前支持的數(shù)據(jù)類型比較少,連 boolean 都不支持,而且我還碰到了 bug,所以目前不建議用。
轉場動畫
如果需要自定義的頁面轉場動畫,使用 Navigation 可以很方便的實現(xiàn)。
這里舉個例子,比如我們需要一個從右向左切入的過場動畫,先創(chuàng)建這個動畫的 xml 文件:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="600"
android:fromXDelta="100%"
android:toXDelta="0" />
</set>
然后我們回到 Navigation 的可視化編輯頁面來,點擊跳轉的線,右邊會出現(xiàn)過場動畫的配置選項,將 xxxx 設為剛才創(chuàng)建的動畫:
這么簡單就搞定了,效果如下:
Navigation 的使用介紹就到這里。
思考
你可能已經(jīng)明白,Navigation 主要是用來處理 Fragment 的跳轉,所以說它并不是用來代替 startActivity,而是用來代替 FragmentTransaction 的相關操作。
在官方文檔里,可以看到一個將傳統(tǒng)跳轉遷移到 Navigation 的建議。我簡單理解為,將原本兩個 Activity 之間的跳轉,逐漸修改為使用一個 Activity 作為容器,用兩個 Fragment 作為頁面跳轉。
看到這里,我聯(lián)想到了在去年,Jake Wharton(目前在谷歌)有這么一個有爭議的言論:
“一個 APP 只需要一個 Activity。”
在過去,要實現(xiàn)這種方式,就需要去解決復雜的 Fragment 堆棧處理,而且早期的 Fragment 坑比較多,處理不好容易出現(xiàn)頁面穿透等問題。現(xiàn)在 Navigation 恰好解決了這些問題。
這一切聯(lián)系起來,是不是能說明官方間接支持了「少用 Activity 多用 Fragment」的做法?你怎么看?