Android Jetpack - 使用 Navigation 管理頁面跳轉

NanBox發表於2018-06-07

在今年的 IO 大會上,釋出了一套叫 Android Jetpack 的程式庫。Android Jetpack 裡的元件大部分我們都接觸過了,其中也有一些全新的元件,其中一個就是 Navigation。

簡介

Navigation 是用來管理 APP 裡頁面跳轉的。起初,我以為它是用來代替 startActivity 的,但其實並不是,大家往下看就知道它的作用了。

另外,iOS 的同學可能會有似曾相識的感覺,Navigation 應該是有借鑑 Storyboard 的。

使用

我們先來看看 Navigation 的實現過程。

新增依賴

首先,需要使用 Android Studio 3.2 以上版本才能使用 Navigation。

在 build.gradle 中新增依賴:

implementation "android.arch.navigation:navigation-fragment:$nav_version"
implementation "android.arch.navigation:navigation-ui:$nav_version"
複製程式碼

建立 navigation xml 檔案

使用 「Android Resource File」建立 xml 檔案的時候,可以看到在型別裡,多了一個 Navigation 的選項:

Android Jetpack - 使用 Navigation 管理頁面跳轉

建立成功後,就來到了文章開頭的那個一個視覺化的操作介面。點選左上角的新增小圖示,會出現 Activity 和 Fragment,我們這裡新增兩個 Activity 和兩個 Fragment:

Android Jetpack - 使用 Navigation 管理頁面跳轉

配置 Action

Fragment 的右邊有個小圓圈,點選並拖到另一個頁面,這樣我們就給這個 Fragment 新增了一個跳轉行為,也就是 Action。

但是可以發現,Activity 的右邊是沒有這個小圓圈的,所以 Navigation 並不能處理從 Activity 發起的跳轉。

左上角有個小房子的是顯示的第一個頁面,但由於 Activity 無法發起跳轉,所以這裡把 MainActivity 刪除,把 MainFragment 作為主頁面,並給它新增跳轉到 SecondFragment 和 SecondActivity 的 Action:

Android Jetpack - 使用 Navigation 管理頁面跳轉

自動生成的 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

現在,我們第一個頁面是 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,實現跳轉,比如:

NavHostFragment
            .findNavController(this)
            .navigate(R.id.action_firstFragment_to_secondFragment)
複製程式碼

在簡單配置了兩個跳轉後,看一下目前的效果:

Android Jetpack - 使用 Navigation 管理頁面跳轉

傳參

頁面的跳轉少不了資料的傳遞,使用 Navigation,和我們原來的跳轉一樣,可以通過 Bundle 來傳遞引數:

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 檔案中配置傳參,但這種方式目前支援的資料型別比較少,連 boolean 都不支援,而且我還碰到了 bug,所以目前不建議用。

轉場動畫

如果需要自定義的頁面轉場動畫,使用 Navigation 可以很方便的實現。

這裡舉個例子,比如我們需要一個從右向左切入的過場動畫,先建立這個動畫的 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 的視覺化編輯頁面來,點選跳轉的線,右邊會出現過場動畫的配置選項,將 xxxx 設為剛才建立的動畫:

Android Jetpack - 使用 Navigation 管理頁面跳轉

這麼簡單就搞定了,效果如下:

Android Jetpack - 使用 Navigation 管理頁面跳轉

Navigation 的使用介紹就到這裡。

思考

你可能已經明白,Navigation 主要是用來處理 Fragment 的跳轉,所以說它並不是用來代替 startActivity,而是用來代替 FragmentTransaction 的相關操作。

在官方文件裡,可以看到一個將傳統跳轉遷移到 Navigation 的建議。我簡單理解為,將原本兩個 Activity 之間的跳轉,逐漸修改為使用一個 Activity 作為容器,用兩個 Fragment 作為頁面跳轉。

看到這裡,我聯想到了在去年,Jake Wharton(目前在谷歌)有這麼一個有爭議的言論:

“一個 APP 只需要一個 Activity。”

在過去,要實現這種方式,就需要去解決複雜的 Fragment 堆疊處理,而且早期的 Fragment 坑比較多,處理不好容易出現頁面穿透等問題。現在 Navigation 恰好解決了這些問題。

這一切聯絡起來,是不是能說明官方間接支援了「少用 Activity 多用 Fragment」的做法?你怎麼看?

相關文章