一文看懂Navigation,從使用入門到常見(jiàn)問(wèn)題再到示例項(xiàng)目

前言

這可以說(shuō)是 Jetpack Navigation 胎教級(jí)文章,從 使用入門常見(jiàn)問(wèn)題 再到 示例項(xiàng)目 一篇文章全部包括猛计。

Navigation及其用途

NavigationAndroid Jetpack 組件之一,主要是用于 Fragment 路由導(dǎo)航的框架爆捞,通過(guò) Navigation 我們可以設(shè)計(jì)出單 Activity 應(yīng)用架構(gòu)奉瘤。

Navigation 由以下三個(gè)關(guān)鍵部分組成:

  • NavHost:顯示 Fragment 的容器。
  • NavController:在 NavHost 中管理應(yīng)用導(dǎo)航的對(duì)象煮甥。
  • 導(dǎo)航圖:配置所有導(dǎo)航相關(guān)信息的 XML 資源毛好。跟我們常用的 AndroidManifest.xml 類似望艺。

所以 Navigation 的使用即在 導(dǎo)航圖 配置 Fragment 相關(guān)信息,再告訴 NavController 導(dǎo)航至特定目標(biāo)肌访, NavController 便會(huì)在 NavHost 中顯示相應(yīng)目標(biāo)找默。

使用入門

請(qǐng)向應(yīng)用的 build.gradle 文件添加以下依賴項(xiàng):

dependencies {
  val nav_version = "2.3.5"
  // Kotlin
  implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
  implementation("androidx.navigation:navigation-ui-ktx:$nav_version")
}

添加導(dǎo)航圖,請(qǐng)執(zhí)行以下操作:

  1. 在“Project”窗口中吼驶,右鍵點(diǎn)擊 res 目錄惩激,然后依次選擇 New > Android Resource File。此時(shí)系統(tǒng)會(huì)顯示 New Resource File 對(duì)話框蟹演。
  2. File name 字段中輸入名稱风钻,例如“nav_graph”。
  3. Resource type 下拉列表中選擇 Navigation酒请,然后點(diǎn)擊 OK骡技。

當(dāng)您添加首個(gè)導(dǎo)航圖時(shí),Android Studio 會(huì)在 res 目錄內(nèi)創(chuàng)建一個(gè) navigation 資源目錄羞反。該目錄包含您的導(dǎo)航圖資源文件(例如 nav_graph.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"
            android:id="@+id/nav_graph">

</navigation>

向 Activity 添加 NavHost

請(qǐng)注意以下幾點(diǎn):

  • android:name 屬性包含 NavHost 實(shí)現(xiàn)的類名稱。
  • app:navGraph 屬性將 NavHostFragment 與導(dǎo)航圖相關(guān)聯(lián)昼窗。導(dǎo)航圖會(huì)在此 NavHostFragment 中指定用戶可以導(dǎo)航到的所有目的地是趴。
  • app:defaultNavHost="true" 屬性確保您的 NavHostFragment 會(huì)攔截系統(tǒng)返回按鈕。請(qǐng)注意,只能有一個(gè)默認(rèn) NavHost。如果同一布局(例如捂龄,雙窗格布局)中有多個(gè)宿主环础,請(qǐng)務(wù)必僅指定一個(gè)默認(rèn) NavHost
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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
        .../>

     <androidx.fragment.app.FragmentContainerView
        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
        .../>

</androidx.constraintlayout.widget.ConstraintLayout>

向?qū)Ш綀D添加并導(dǎo)航到目的地

在導(dǎo)航圖中,操作由 <action> 元素表示。操作至少應(yīng)包含自己的 ID 和用戶應(yīng)轉(zhuǎn)到的目的地的 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="@string/label_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="@string/label_blank_2"
        tools:layout="@layout/fragment_blank_fragment2" />
</navigation>

導(dǎo)航到目的地是使用 NavController 完成的温赔,它是一個(gè)在 NavHost 中管理應(yīng)用導(dǎo)航的對(duì)象。通過(guò) navigate(int) 接受操作或目的地的資源 ID 作為參數(shù)帅刀。

val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
navController.navigate(R.id.action_blankFragment_to_blankFragment2)

導(dǎo)航圖規(guī)則

  1. 導(dǎo)航圖之間可以通過(guò) id 跳轉(zhuǎn)到對(duì)應(yīng)導(dǎo)航圖的 startDestination
  2. 同個(gè)導(dǎo)航圖之內(nèi)可以通過(guò) action 互相跳轉(zhuǎn)
  3. 不同導(dǎo)航圖之間可以通過(guò)深層鏈接跳轉(zhuǎn)

以上就是 Navigation 的基本使用让腹,其他細(xì)節(jié)可到官網(wǎng)查看Navigation 組件使用入門 | Android Developers

常見(jiàn)問(wèn)題

通過(guò)上面的介紹我們對(duì) Navigation 有了一定的了解,但當(dāng)你按照文檔進(jìn)行開(kāi)發(fā)時(shí)扣溺,便會(huì)發(fā)現(xiàn) Navigation 存在的一些小問(wèn)題骇窍。

因?yàn)?Navigation 是通過(guò) replace 的方式去替換 Fragment ,即每次 Fragment 的切換都是重新創(chuàng)建的 锥余,而問(wèn)題也隨之而來(lái)腹纳。

重復(fù)請(qǐng)求數(shù)據(jù)

既然我們的項(xiàng)目用了 Navigation ,那么 Jetpack 全家桶肯定是一個(gè)不能少的了。 Fragment 的重建并不會(huì)使ViewModel 被銷毀嘲恍,所以 LiveData 的數(shù)據(jù)依然保存著足画,頁(yè)面重建后會(huì)把數(shù)據(jù)重新渲染到頁(yè)面上,所以在視覺(jué)上我們根本感覺(jué)不到被頁(yè)面被重建了佃牛,但是相應(yīng)的初始化邏輯會(huì)繼續(xù)執(zhí)行會(huì)重新請(qǐng)求數(shù)據(jù)淹辞,這就造成了資源的浪費(fèi)。既然知道了原因俘侠,那如何避免這個(gè)問(wèn)題呢象缀?

最簡(jiǎn)單的方法是在請(qǐng)求數(shù)據(jù)前加個(gè)空判斷,代碼如下:

if (viewModel.data.value == null) {
    viewModel.getData()
}

乍一看會(huì)覺(jué)得這方案好low呀爷速,請(qǐng)大家先別著急聽(tīng)我慢慢道來(lái)央星。在此之前我亦是采用過(guò)其他方案,比如對(duì) LiveData 進(jìn)行包裝惫东,隨著業(yè)務(wù)需求的不斷變化莉给,你就要去維護(hù)它這是不能接受的。最終采用上述的方案廉沮,越簡(jiǎn)單越不會(huì)出錯(cuò)(UNIX設(shè)計(jì)哲學(xué))颓遏。

過(guò)渡動(dòng)畫卡頓

導(dǎo)致過(guò)渡動(dòng)畫卡頓的原因當(dāng)然也是 Fragment 重建咯,Fragment 重建后需要重新渲染頁(yè)面废封,卡那么一下也是正持莶矗現(xiàn)象丧蘸。既然知道了原因漂洋,那如何避免這個(gè)問(wèn)題呢?

最簡(jiǎn)單的方法是在動(dòng)畫執(zhí)行完成后渲染頁(yè)面力喷,代碼如下:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    view.postDelayed({
        initLoad()
    }, delayedLoad) //delayedLoad為設(shè)置的過(guò)度動(dòng)畫時(shí)間
}

依舊是UNIX設(shè)計(jì)哲學(xué)刽漂,當(dāng)然上述的方案并不是唯一解,其他方案請(qǐng)自行搜索學(xué)習(xí)哈??

(ps:官方為什么不解決上面這些問(wèn)題呢弟孟?個(gè)人的猜測(cè)是官方還沒(méi)有找一個(gè)優(yōu)雅的解決方案贝咙,期待后續(xù)的版本的優(yōu)化吧 ??)

示例項(xiàng)目




Thanks

以上就是本篇文章的全部?jī)?nèi)容,如有問(wèn)題歡迎指出拂募,我們一起進(jìn)步庭猩。
如果喜歡的話希望點(diǎn)個(gè)贊吧,您的鼓勵(lì)是我前進(jìn)的動(dòng)力陈症。
謝謝~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蔼水,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子录肯,更是在濱河造成了極大的恐慌趴腋,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異优炬,居然都是意外死亡颁井,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門蠢护,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)雅宾,“玉大人,你說(shuō)我怎么就攤上這事葵硕⌒阌郑” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵贬芥,是天一觀的道長(zhǎng)吐辙。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蘸劈,這世上最難降的妖魔是什么昏苏? 我笑而不...
    開(kāi)封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮威沫,結(jié)果婚禮上贤惯,老公的妹妹穿的比我還像新娘。我一直安慰自己棒掠,他們只是感情好孵构,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著烟很,像睡著了一般颈墅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上雾袱,一...
    開(kāi)封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天恤筛,我揣著相機(jī)與錄音,去河邊找鬼芹橡。 笑死毒坛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的林说。 我是一名探鬼主播煎殷,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼腿箩!你這毒婦竟也來(lái)了豪直?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤度秘,失蹤者是張志新(化名)和其女友劉穎顶伞,沒(méi)想到半個(gè)月后饵撑,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡唆貌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年滑潘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锨咙。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡语卤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出酪刀,到底是詐尸還是另有隱情粹舵,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布骂倘,位于F島的核電站眼滤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏历涝。R本人自食惡果不足惜诅需,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望荧库。 院中可真熱鬧堰塌,春花似錦、人聲如沸分衫。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蚪战。三九已至牵现,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間屎勘,已是汗流浹背施籍。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工居扒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留概漱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓喜喂,卻偏偏與公主長(zhǎng)得像瓤摧,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子玉吁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容