開始使用 Navigation


由於官網暫時沒有中文版本,所以在這裏根據內容抽空做了一些中文的版本.

原文首發於https://leonchen1024.com/2019/07/04/Navigation-Getting-Started/#more

開始使用

依賴引入

dependencies {
    def nav_version = "2.1.0-alpha05"

    implementation "androidx.navigation:navigation-fragment:$nav_version" // For Kotlin use navigation-fragment-ktx
    implementation "androidx.navigation:navigation-ui:$nav_version" // For Kotlin use navigation-ui-ktx
}

最新版本清關注官方版本 https://developer.android.google.cn/jetpack/androidx/releases/navigation#declaring_dependencies

創建一個導航圖

Navigation 發生在 app 內目的地之間的跳轉.這些目的地通過action 進行連接.

一個導航圖是一個包含了你所有目的地和 action 的資源文件.這個圖表代表了你所有的導航路徑.

下圖是一個導航圖的示例,


原文首發於https://leonchen1024.com/2019/07/04/Navigation-Getting-Started/#more

圖中的 1 是 app 中的不同的內容

2 是導航的路徑

通過以下步驟添加導航圖

  1. 在項目窗口中右鍵 res 文件夾選擇 New > Android Resource File
  2. 輸入文件名
  3. Resource type 中選擇 Navigation ,確認.

生成的文件會保存在 navigation 文件夾下面.

Navigation Editor

當你添加了導航圖的時候,可以使用 Navigation Editor 來進行可視化操作,或者對 xml 文件直接進行修改

圖中序號代表的東西 :

1: 目的地面板, 列出你的導航所有者和當前導航圖中所有的目的地.

2: 圖編輯器 , 可以通過下方的 tab 切換成可視化編輯和文本編輯模式.

3: 屬性 , 展示當前選擇項的屬性.

當你使用文本編輯的時候,應該會看到類似以下的結構:

<?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"
            android:id="@+id/nav_graph">

</navigation>

節點是根節點,當你添加了目的地和 action 的時候它們對應的和都會以 的子元素的形式展現出來. 如果你使用了嵌套圖的時候,它會以子的形式展現.

添加一個 NavHost 到 activity

Navigation 模塊中的一個核心部分就是 navigation host . navigation host 是一個空的容器,當用戶在你的 app 內導航的時候目的地將會在這個容器中進出.

一個 navigation host 必須從 NavHost 中衍生而來.模塊中有一個默認的實現,NavHostFragment , 處理了 fragment 目的地的轉場.

注意: Navigation 模塊的設計是爲了處理一個 Activity 和多個目的地Fragment 的情況,每一個 Activity 都會關聯一個導航圖並擁有自己的NavHostFragment 來響應內容的切換轉場.

通過 xml 添加一個NavHostFragment

下面的 xml 展示了一個 NavHostFragment 作爲主 activity 的一部分的情況:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.Toolbar
        .../>

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"

        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        .../>

</android.support.constraint.ConstraintLayout>

注意以下內容:

  • android:name 屬性包含了你的 NavHost 實現的類名
  • app:navGraph 屬性將 NavHostFragment 和一個 導航圖進行關聯,導航圖會顯示在這個 NavHostFragment 中用戶能到達的所有目的地.
  • app:defaultNavHost="true" 屬性確保你的 NavHostFragment 可以攔截系統的返回事件,要注意只有一個 NavHost 可以作爲默認攔截.

你也可以通過以下方法來通過佈局編輯器來添加一個 NavHostFragment 到 activity 中.

  • 打開你的 activity 的 xml 文件,並使用佈局編輯器.
  • Palette 面板中選擇Containers目錄,或者搜索 “NavHostFragment”
  • 拖動 NavHostFragment 到你的佈局中.
  • Navigation Graphs 的彈窗中,選擇對應的導航圖和 NavHostFragment 關聯,並點 ok.

原文首發於https://leonchen1024.com/2019/07/04/Navigation-Getting-Started/#more

添加目的地到導航圖

你可以在 Fragment 或者 activity 中添加目的地,也可以通過導航編輯器來添加目的地或者佔位符以便在後面用 activity 或 Fragment 來替換.

從現有的 activity 或 Fragment 中添加目的地

如果你想添加現有的目的地到你的導航圖中,點擊 New Destination img, 再出現的下拉框中選擇你想要添加的目的地.然後你就可以在 Design 視圖中看到該目的地的預覽圖.以及對應的xml 數據出現在你的 Text 視圖中.

創建一個新的Fragment目的地

使用以下步驟創建一個新的 Fragment 目的地:

  1. 在導航編輯器中點擊 New Destination圖標 img, 然後點擊 Create new destination .
  2. New Android Component 彈窗中創建你的 Fragment ,

從 DialogFragment 中創建一個目的地

如果你已經有了 DialogFragment 的話,你可以使用 <dialog> 元素將它添加到導航圖中.代碼如下:

<?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"
            android:id="@+id/nav_graph">

...


<dialog
    android:id="@+id/my_dialog_fragment"
    android:name="androidx.navigation.myapp.MyDialogFragment">
    <argument android:name="myarg" android:defaultValue="@null" />
        <action
            android:id="@+id/myaction"
            app:destination="@+id/another_destination"/>
</dialog>

...

</navigation>

佔位目的地

你可以使用佔位符來代表沒有實現的目的地,佔位符也是目的地的視覺表示.在導航編輯器中可以像使用目的地一樣使用佔位符.

注意:在你運行你的 app 之前你必須要將 class 設置成已經存在的目的地.否則會導致運行時異常.

解析目的地

點擊任意的目的地,並注意以下屬性:

  • Type : 表明這個目的地是使用 fragment, activity, 還是其他源碼中的自定義類實現.
  • Label : 目的地的 xml 資源文件名
  • ID: 目的地的 ID ,可以用於在代碼中引用該目的地
  • Class : 下拉窗中展示了所有與該目的地相關聯的類.可以選擇不同的選項將類與其他目的地類型關聯.

在 xml 文件中的展示如下:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    app:startDestination="@id/blankFragment">
    <fragment
        android:id="@+id/blankFragment"
        android:name="com.example.cashdog.cashdog.BlankFragment"
        android:label="Blank"
        tools:layout="@layout/fragment_blank" />
</navigation>

設置一個屏幕作爲起始目的地

起始目的地是用戶打開你的app看到的第一個屏幕,並且也是用戶退出你的app看到的最後一個頁面.導航編輯器使用一個房子圖標 img 來代表這個起始目的地.

一旦你擁有所有的目的地,你可以通過以下步驟選擇一個作爲起始目的地:

  1. Design 標籤頁面下,點擊一個目的地使它高亮.
  2. 點擊 Assign start destination 按鈕 img .或者,右鍵目的地並點擊 Set as Start Destination.

原文首發於https://leonchen1024.com/2019/07/04/Navigation-Getting-Started/#more

連接目的地

action 是目的地之間的一個邏輯連接.action 在導航圖裏用箭頭表示.action 通常將一個目的地連接到另一個,儘管你也可以創建一個 global actions 來把你引導到你的app中的任意一個目的地.

通過action ,你可以用不同的路徑來代表用戶可以如何在你的app中導航.要注意,要實際導航到某個目的地的時候,你仍然需要編寫代碼來實現這個導航.這在 Navigate to a destination 中會有介紹.

你可以通過以下步驟使用導航編輯器來連接兩個目的地:

  1. Design 標籤下,高亮你要作爲起點的目的地.它的右側會出現一個圓圈
    img

  2. 點擊這個圓圈並拖動鼠標指針到你想要作爲終點的目的地,然後釋放鼠標.兩個目的地之間會出現一條線來代表這個action
    img

  3. 點擊箭頭來高亮這個 action . Attributes 面板中顯示了下面的屬性:

    • Type : Action 的類型
    • ID : action的ID
    • Destination : 包含了目的地fragment 或者 activity 的 ID
  4. 點擊 Text 標籤來切換到 XML 視圖. 一個 action 元素會被添加到源目的地中.這個action擁有一個 ID 和一個目的地屬性包含了下一個目的地的ID 如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <navigation xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:android="http://schemas.android.com/apk/res/android"
        app:startDestination="@id/blankFragment">
        <fragment
            android:id="@+id/blankFragment"
            android:name="com.example.cashdog.cashdog.BlankFragment"
            android:label="fragment_blank"
            tools:layout="@layout/fragment_blank" >
            <action
                android:id="@+id/action_blankFragment_to_blankFragment2"
                app:destination="@id/blankFragment2" />
        </fragment>
        <fragment
            android:id="@+id/blankFragment2"
            android:name="com.example.cashdog.cashdog.BlankFragment2"
            android:label="fragment_blank_fragment2"
            tools:layout="@layout/fragment_blank_fragment2" />
    </navigation>
    

在你的導航圖裏,action是使用 <action> 元素代表的. 一個 action 裏至少要包含它自己的ID 和用戶要導航到的目的地的ID .

原文首發於https://leonchen1024.com/2019/07/04/Navigation-Getting-Started/#more

導航到目的地

可以使用 NavController 導航到一個目的地,它是一個通過 NavHost 來管理app導航的對象.每一個 NavHost 都有一個它對應的 NavController . NavController 提供一些不同的方式來導航到一個目的地,在下面的章節中將會詳細介紹.

使用以下方法之一來從 fragment ,activity,或者view 中獲取一個 NavController :

Kotlin:

Java:

使用 ID 導航

navigate(int) 使用 action 或者目的地的資源 ID來導航. 下面的例子展示瞭如何導航到 ViewTransactionsFragment .

viewTransactionsButton.setOnClickListener { view ->
   view.findNavController().navigate(R.id.viewTransactionsAction)
}
viewTransactionsButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Navigation.findNavController(view).navigate(R.id.viewTransactionsAction);
    }
});

注意: 當你使用 ID 來導航的時候,我們強烈推薦你儘可能使用 action. action 在你的導航圖裏提供了額外的可視信息,它展示了你的目的地是如何和其它目的地連接起來的.通過創建 action ,你可以使用 Safe Args-generated operations 來替換資源ID ,提供額外的編譯時安全性.通過使用 action 你也可以在目的地之間使用轉場動畫.詳細可以查看 Animate transitions between destinations.

對於按鈕,你也可以使用 Navigation 類的 createNavigateOnClickListener() 方法來便捷的導航到一個目的地,如下面的例子所示:

button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null))
button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null));

爲了處理其它的共用 UI 模塊,比如頂部的 top app bar 和 bottom navigation , 詳見 Update UI components with NavigationUI.

通過 URI 進行導航

你可以通過使用 navigate(Uri) 來直接導航到一個 implicit deep link destination, 如下:

val navController = findNavController()
val deeplink = Uri.parse("android-app://androidx.navigation.app/profile")
findNavController().navigate(deeplink)
Uri deeplink = Uri.parse("android-app://androidx.navigation.app/profile");
view.findNavController().navigate(deeplink);

和使用 action 或者 目的地ID 不同,你可以導航到你的導航圖中的任意一個 URI ,無論目的地是否在當前導航圖中可見.你可以導航到當前導航圖或者一個不同的導航圖中的目的地.

當使用 URI 進行導航的時候,回退棧不會被重置.與其他的 deep link navigation 不同,

When navigating using URI, the back stack is not reset. This is unlike other deep link navigation, where the back stack is replaced when navigating. popUpTo and popUpToInclusive, however, still remove destinations from the back stack just as though you had navigated using an ID.

導航還有回退棧

Android 維護了一個 back stack 包含了你訪問的目的地.你app 的第一個 目的地在用戶打開 app 的時候就會放進棧中. 每一次調用 navigate() 方法會把另一個目的地放到棧頂. 當調用 NavController.navigateUp()NavController.popBackStack() 來進行向上或向後的操作的時候,會移除棧頂的目的地.

popUpTo 和 popUpToInclusive

當你使用 action 來進行導航的時候,你可以選擇從後退棧中彈出額外的目的地.舉個例子,如果你的應用有一個登錄流程,一旦用戶登錄了,你應該從回退棧中彈出所有登錄相關的目的地,這樣可以保證用戶回退的時候不會又回到登錄流程中.

爲了在一個目的地導航到另一個目的地的時候彈出目的地,可以添加一個 app:popUpTo 屬性到關聯的 <action> 元素中. app:popUpTo 這個屬性告訴 Navigation 在調用 navigate() 的時候從回退棧中彈出一些目的地.這個屬性的值是 要在回退中保留的頂層目的地的ID .

你也可以使用 app:popUpToInclusive="true" 來聲明這個 app:popUpTo 彈出操作包含了這個屬性聲明的ID 對象.

popUpTo 示例:循環邏輯

我們假設你的app擁有3個目的地—A,B,C.還有從 A 到 B ,B 到 C ,以及 C 回到A 的action.對應的導航圖如下:

img

當你每一次調用導航action的時候,都會往回退棧中添加一個目的地.如果你按照這個流程重複導航的花,你的回退棧中將會包含很多這個重複的集合目的地(A,B,C,A,B,C,A…) . 要避免這種重複的話,可以在從C 到A 的action中指定 app:popUpTo 還有 app:popUpToInclusive 屬性,如下:

<fragment
    android:id="@+id/c"
    android:name="com.example.myapplication.C"
    android:label="fragment_c"
    tools:layout="@layout/fragment_c">

    <action
        android:id="@+id/action_c_to_a"
        app:destination="@id/a"
        app:popUpTo="@+id/a"
        app:popUpToInclusive="true"/>
</fragment>

在到達了目的地 C 之後,回退棧中包含了目的地 A,B,C 的實例.當導航回目的地 A 的時候,我們同時 popUpTo A ,這意味着我們在導航的時候從回退棧中刪除了 B,C. 伴隨着 app:popUpToInclusive="true" 我們同時彈出了回退棧中的第一個 A.要注意如果你沒有使用 app:popUpToInclusive="true" 這個屬性的話,你的回退棧中將會包含兩個A 的實例.

More information

If you encounter any issues with Navigation, please submit feedback via one of the following channels:

For information on how to provide the most helpful information in bug reports, see the following links:

原文首發於https://leonchen1024.com/2019/07/04/Navigation-Getting-Started/#more

Refernce

https://developer.android.google.cn/guide/navigation/navigation-getting-started

About Me

我的博客 leonchen1024.com

我的 GitHub https://github.com/LeonChen1024

微信公衆號

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章