騰訊等大廠面試最喜歡問的Android-Jetpack架構組件-—-Navigation詳解 [轉]

前言

Navigation 直接翻譯即爲導航,它是 Android Jetpack 組件之一,讓單 Activity 應用成爲首選架構。應用內Fragment頁面的跳轉則由 Navigation 來處理,開發者無需在處理 FragmentTransaction 的複雜性以及相關的轉場動畫。

具體使用

在app的gradle.build中添加依賴:

def nav_version = "2.1.0"implementation "androidx.navigation:navigdef nav_version = "2.1.0"

implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

首先我們定義三個Fragment,分別爲Fragment1,Fragment2和Fragment3。實現邏輯爲Fragment1點擊跳轉到Fragment2,Fragment2點擊跳轉到Fragment3,Fragment3跳轉到Fragment1同時點擊返回鍵時也返回到Fragment1。

** navigation: 導航視圖XML的根結點。裏面定義相關fragment的跳轉邏輯。**

首先需要在res資源目錄下新建 navigation 文件夾,右鍵新建一個Navigation resource file命名爲nav_graph_main.xml。

文件左下腳分爲兩個Tab:Design和Text。Design視圖是可視化的,可以直接選擇相關fragment。Text視圖是我們手寫相關配置。

我們看下定義的nav_graph_main.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/fragment1">

    <fragment
        android:id="@+id/fragment1"
        android:name="com.jetpack.jetpackdemo.navigation.fragment.Fragment1"
        android:label="Fragment1"
        tools:layout="@layout/fragment1_layout">

        <action
            android:id="@+id/fragment1_action"
            app:destination="@+id/fragment2" />

    </fragment>

    <fragment
        android:id="@+id/fragment2"
        android:name="com.jetpack.jetpackdemo.navigation.fragment.Fragment2"
        android:label="Fragment2"
        tools:layout="@layout/fragment2_layout">

        <action
            android:id="@+id/fragment2_action"
            app:destination="@+id/fragment3" />

    </fragment>

    <fragment
        android:id="@+id/fragment3"
        android:name="com.jetpack.jetpackdemo.navigation.fragment.Fragment3"
        android:label="Fragment3"
        tools:layout="@layout/fragment3_layout">

        <action
            android:id="@+id/fragment3_action"
            app:popUpTo="@id/fragment1" />

    </fragment>

</navigation>

navigation根節點中有個startDestination字段,他表示的是默認展示的是哪一個頁面。通過fragment標籤來定義要路由的相關頁面。id爲fragment唯一標識。name爲包名,必須保證正確。layout爲fragment的佈局文件,配置後方便在Design視圖中查看。

fragment中配置了子節點 action 。action表示的就是具體要路由的行爲。同樣id也是其唯一標識,destination表示的是目的地,即需要路由到具體的某一個頁面。popUpTo表示彈出到某一個頁面。action還有其他的屬性比如配置動畫等,具體請看Demo。

** NavHostFragment是導航視圖的展示容器。**

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

name爲固定寫法,必須指明爲

androidx.navigation.fragment.NavHostFragment

defaultNavHost字段表示是否攔截返回按鍵操作。

若爲true,需要的Activity中重寫onSupportNavigateUp方法。

因爲默認情況下返回鍵是不會回退fragment頁面的。

override fun onSupportNavigateUp(): Boolean {
    return findNavController(R.id.nav_host_fragment).navigateUp()
}
navGraph字段即爲我們配置的navigation導航視圖。

NavController

通過findNavController來獲取NavController,通過controller的navigate或者navigateUp進行頁面之間的路由操作。

那麼在三個頁面的點擊按鈕的邏輯就是挑戰相應的頁面:

mBtn.setOnClickListener {
    Navigation.findNavController(it).navigate(R.id.fragment1_action)

}

通過指定action的id來告訴Navigation跳轉的邏輯。其他頁面也是一樣。

最終效果:

我們來總結下 navigation、NavHostFragment以及NavController之間的關係。

navigation就是規劃了很多的路線,而這些路線需要在NavHostFragment中才能進行展示。展示後這麼多的路線該怎麼走呢,決定權就在NavController手中了,就像是方向盤一樣,控制着該走哪一個路線。

傳遞參數

在上文中我們講解了navigation相關的知識,其中還有一個子標籤:argument。是用來定義參數的。比如我們在fragment2標籤中添加argument標籤如下:

<argument
    android:name="name"
    android:defaultValue="navigation導航"
    app:argType="string"
    app:nullable="false" />

那麼在fragment1跳轉到fragment2的時候就可以攜帶參數了。其中 name 表示參數名稱。defaultValue即爲默認值。argType爲參數的類型。nullable表示是否可以爲空。

fragment之間傳遞參數有兩種方式:

  • 傳統的Bundle方式
  • 通過谷歌提供的safeArgs

傳統的Bundle方式

通過Bundle來設置和獲取參數。

在fragment1中進行設置:

mBtn.setOnClickListener {
    //如果要使用 xml中argument的默認值則直接new Bundle() 傳入即可
    val args = Bundle()
    args.putString("name","通過bundle傳遞參數")

    Navigation.findNavController(it).navigate(R.id.fragment1_action, args)

}

在fragment2中進行獲取參數:

val args = arguments
val name = args?.getString("name")

mTvName.text = name

這樣就可以將參數進行傳遞了。上面這種方式大家有沒有覺得有什麼問題呢?

參數名稱 “name” 我們在三處進行的手動填寫。這樣會很容易導致拼寫錯誤以及修改的時候容易漏改。很不友好。所以谷歌給我們提供了一個插件:safeArgs。下面我們來看下具體使用。

safeArgs

首先需要進行配置,在項目的 build.gradle 中添加classpath配置:

dependencies {
    classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0'
}

再在app的 build.gradle添加 apply plugin。

apply plugin: 'androidx.navigation.safeargs'

項目重新構建後會知道爲fragment生成後綴爲 Directions的文件。併爲navigation中有 argument 標籤的fragment自動生成後綴爲Args的文件。

通過後綴爲 Directions的文件進行參數的設置。後綴爲Args的文件進行參數的獲取。

fragment1中進行設置:

mBtn.setOnClickListener {
    
    val args = Fragment1Directions.fragment1Action().setName("通過safeArgs進行參數傳遞")
    Navigation.findNavController(it).navigate(R.id.fragment1_action, args.arguments)

}

fragment2中進行獲取:

val name = Fragment2Args.fromBundle(arguments!!).name
mTvName.text = name

這樣就完成了fragment之間參數的傳遞。完全避免了手動設置參數的邏輯。直接通過setter和getter進行參數的操作。

總結

總體來說Navigation的使用並不複雜,它讓我們單Activity架構成爲可能,無需關心具體的fragment的跳轉邏輯。但是同樣也是有問題的,通過源碼分析我們知道

在NavHostFragment的onCreateView中是創建了FrameLayout,也就是說其實真正的容器是FrameLayout。在創建FragmentNavigator的時候內部使用的是replace這個API,而不是show和hide。這就會導致fragment每次生命週期都會重新執行。所以和ViewModel結合使用效果應該更好。

本文在開源項目:https://github.com/xieyuliang/Note-Android中已收錄,裏面包含不同方向的自學編程路線、面試題集合/面經、及系列技術文章等,資源持續更新中……

這次就分享到這裏吧,下篇見

原文鏈接:https://blog.csdn.net/m0_46962786/article/details/109119746

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