Navigation簡化了導航的實現,同時也幫助您可視化應用程序的導航流,Naviagtion組件提供瞭如下的功能:html
在Codelab中你可使用demo運行效果:android
全部的Activity和Fragment都已經爲你建立好了,你可使用導航組件鏈接它們,並在鏈接過程當中實現如下功能:git
Githubgithub
$ git clone https://github.com/googlecodelabs/android-navigation
複製代碼
Navigation組件主要包括三個部分shell
當你導航時,你將使用導航控制器對象,告訴它你想去哪裏,或者你想在你的導航圖中走什麼路徑。而後,導航控制器將在NavHostframent中顯示適當的目的地。編程
這是基本的想法。讓咱們重新的導航圖資源開始,看看這在實踐中是什麼樣子的。設計模式
Navigation組件中有一個Destinations的概念,它是一個能夠在你APP中導航到的任意的地方,一般是一個Activity或者Fragment,默認是支持使用的,若是你須要你能夠本身自定義目標類型。安全
一個導航圖是一種新的資源類型,它直觀地顯示從給定目的地能夠到達的全部目的地。在AS的Navigation Editor中展現,下面是爲APP建立的起始導航圖的一部分:bash
res/navigation/mobile_navigation.xml
文件在設計模式下你對導航的設計改變均可以在XML佈局文件中看到,點擊Text按鈕app
<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/home_dest">
<!-- ...tags for fragments and activities here -->
</navigation>
複製代碼
注意:
<navigation>
標籤是整個視圖的跟佈局<navigation>
包括一個或者更多的目標界面,通常爲Activity
或者fragment
app:startDestination
是一個目標界面的特殊屬性,表明着啓動的默認頁面接下來
<fragment
android:id="@+id/flow_step_one_dest"
android:name="com.example.android.codelabs.navigation.FlowStepFragment"
tools:layout="@layout/flow_step_one_fragment">
<argument
.../>
<action
android:id="@+id/next_action"
app:destination="@+id/flow_step_two_dest">
</action>
</fragment>
複製代碼
注意
android:id
是你定義的目標界面的id屬性ndroid:name
是你的目標界面的整個包路徑名tools:layout
指定應在圖形編輯器中顯示的佈局代碼中的<action>
<argument>
下面會詳細介紹
demo中只有不多的目標接 main,下面咱們能夠新增一個目標界面,首先你須要本身先建立好一個Fragment或者Activity:
每一步的代碼都在demo中能夠找到,你能夠在TODO的地方編寫代碼或與註釋掉的代碼進行比較
res/navigation/mobile_navigation.xml
文件,點擊Desgin按鈕mobile_navigation.xml
<fragment
android:id="@+id/settings_dest"
android:name="com.example.android.codelabs.navigation.SettingsFragment"
android:label="@string/settings"
tools:layout="@layout/settings_fragment" />
複製代碼
要遵循咱們的命名約定,請將ID從默認設置片斷更改成SettingsFragment
Navigation組件遵循導航原則中Navigation指南,指南中建議您使用activities做爲APP的入口,activities包括普通的Navigation例如:BottomNavigation。相比較下,fragment一版做爲一個目標界面。
接下來你須要在佈局中新增NavHostFragment,NavHostFragment經過navGraph與navigation導航編輯器進行關聯
<LinearLayout
.../>
<androidx.appcompat.widget.Toolbar
.../>
<fragment
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/mobile_navigation"
app:defaultNavHost="true"
/>
<com.google.android.material.bottomnavigation.BottomNavigationView
.../>
</LinearLayout>
複製代碼
注意:
android:name="androidx.navigation.fragment.NavHostFragment"
和 app:defaultNavHost="true"
經過NavHostFragment鏈接系統返回鍵app:navGraph="@navigation/mobile_navigation"
經過NavHostFragment鏈接導航的菜單按鈕,導航視圖能夠經過NavHostFragment導航到任意的目標界面最後,當你點擊一個按鈕時,你須要觸發一個導航命令。Navcontroller的觸發navhostfragment中的fragemnt切換。
// Command to navigate to flow_step_one_dest
findNavController().navigate(R.id.flow_step_one_dest)
複製代碼
注意您輸入要導航的目標或操做ID,這些ids是你在xml佈局中定義好的。NavController是很強大的,你能夠調用navigate()
或者popBackStack()
它能夠將代碼轉化爲匹配的導航操做,這些操做基於您要導航的目標類型。例如:你能夠調用navigate()
去跳轉到一個目標界面,NavConroller會調用自身的startActivity()
Kotlin中提供了擴展函數能夠得到與NavHostFragment關聯的NavController對象:
Fragment.findNavController()
view.findNavController()
Activity.findNavController(viewId: Int)
你的NavController和一個NavHostFragment相關聯。所以不管使用哪一種方法,都必須保證fragmengt,view或者view id是NavHostFragmengt自己,或者是它的父類。不然將會拋出異常。
你可使用NavController去切換FlowStepFragment
HomeFragment.kt
onViewCreated()
中find navigate_destination_button
val button = view.findViewById<Button>(R.id.navigate_destination_button)
button?.setOnClickListener {
findNavController().navigate(R.id.flow_step_one_dest, null)
}
複製代碼
您也可使用便捷方法Navigation.createNavigateOnClickListener(@IdRes destId: int, bundle: Bundle)。此方法將構建一個OnClickListener導航到給定目標,並使用一組參數傳遞給目標。
val button = view.findViewById<Button>(R.id.navigate_destination_button)
button?.setOnClickListener(
Navigation.createNavigateOnClickListener(R.id.flow_step_one_dest, null)
)
複製代碼
每一個navigate()
都有一個默認的過渡動畫
能夠經過包含一組來覆蓋默認轉換以及與該調用關聯的其餘屬性NavOptions。 NavOptions使用一種Builder模式,容許您覆蓋和設置所需的選項。還有一個用於NavOptions的ktx DSL,這是你將要使用的。 對於動畫過渡,您能夠在anim資源文件夾中定義XML動畫資源,而後將這些動畫用於過渡。應用代碼中包含一些示例:
更新代碼,以便按導航到目標按鈕顯示自定義過渡動畫。
HomeFragment.kt
NavOptions
並將其傳遞給navigate()
調用navigate_destination_button
val options = navOptions {
anim {
enter = R.anim.slide_in_right
exit = R.anim.slide_out_left
popEnter = R.anim.slide_in_left
popExit = R.anim.slide_out_right
}
}
view.findViewById<Button>(R.id.navigate_destination_button)?.setOnClickListener {
findNavController().navigate(R.id.flow_step_one_dest, null, options)
}
複製代碼
導航系統還容許您經過Actions進行導航。如前所述,導航圖中顯示的線條是動做的直觀表示。
按行動導航比目標導航具備如下優點:
這是鏈接的操做的可視化和XML,flow_step_one_dest
而且flow_step_two_dest
:
<fragment
android:id="@+id/flow_step_one_dest"
android:name="com.example.android.codelabs.navigation.FlowStepFragment">
<argument
.../>
<action
android:id="@+id/next_action"
app:destination="@+id/flow_step_two_dest">
</action>
</fragment>
<fragment
android:id="@+id/flow_step_two_dest"
android:name="com.example.android.codelabs.navigation.FlowStepFragment">
<!-- ...removed for simplicity-->
</fragment>
複製代碼
注意:
flow_step_two_dest
的目標參數; 這是您要導航到的IDflow_step_two_dest
到home_dest
:<fragment
android:id="@+id/home_dest"
android:name="com.example.android.codelabs.navigation.HomeFragment"
.../>
<fragment
android:id="@+id/flow_step_two_dest"
android:name="com.example.android.codelabs.navigation.FlowStepFragment">
<argument
.../>
<action
android:id="@+id/next_action"
app:popUpTo="@id/home_dest">
</action>
</fragment>
複製代碼
注意:
next_action
用於鏈接做用flow_step_two_dest
到home_dest
。您能夠導航使用next_action
ID從任一flow_step_one_dest
或flow_step_two_dest
。這是一個示例,說明操做如何提供抽象級別,並能夠根據上下文導航到不一樣的位置。 使用該popUpTo屬性 - 此操做將從後棧中彈出片斷,直到您到達home_dest
mobile_navigation.xml
在設計模式下打開文件頭home_dest
到flow_step_one_dest
<fragment android:id="@+id/home_dest"
...>
<action android:id="@+id/next_action"
app:destination="@+id/flow_step_one"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
複製代碼
HomeFragment.kt
navigate_action_button
HomeFragment.ktview.findViewById<Button>(R.id.navigate_action_button)?.setOnClickListener(
Navigation.createNavigateOnClickListener(R.id.next_action, null)
)
複製代碼
操做容許您附加NavOptions在導航XML文件中,而不是以編程方式指定它們。
導航組件有一個名爲safe args的Gradle插件,它生成簡單的對象和構建器類,以便對爲目標和操做指定的參數進行類型安全訪問。
Safe args容許您在目標之間傳遞值時刪除這樣的代碼:
val username = arguments?.getString("usernameKey")
複製代碼
而是將其替換爲生成setter和getter的代碼。
val username = args.username
複製代碼
因爲其類型安全性,使用安全args生成類的導航是經過操做導航並在導航期間傳遞參數的首選方式。
build.gradle
文件並注意safe args插件:build.gradle
dependencies {
classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
//...
}
複製代碼
app/build.gradle
文件並注意應用的插件:apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'androidx.navigation.safeargs'
android {
//...
}
複製代碼
mobile_navigation.xml
,並注意如何在flow_step_one_dest
目標中定義參數<fragment
android:id="@+id/flow_step_one_dest"
android:name="com.example.android.codelabs.navigation.FlowStepFragment"
tools:layout="@layout/flow_step_one_fragment">
<argument
android:name="flowStepNumber"
app:argType="integer"
android:defaultValue="1"/>
<action...>
</action>
</fragment>
複製代碼
使用<argument>
標記,safeargs生成一個名爲的類FlowStepFragmentArgs
。
flowStepNumber
,由指定的參數
android:name="flowStepNumber"
,所以生成的類
FlowStepFragmentArgs
將包含一個
flowStepNumber
帶有getter和setter 的變量。
FlowStepFragment.kt
/ Comment out this line
// val flowStepNumber = arguments?.getInt("flowStepNumber")
複製代碼
這種舊式代碼不是類型安全的。最好使用安全的args。 6. 更新FlowStepFragment
以使用代碼生成的類FlowStepFragmentArgs
。這將以FlowStepFragment
類型安全的方式獲取參數:
FlowStepFragment.kt
val safeArgs: FlowStepFragmentArgs by navArgs()
val flowStepNumber = safeArgs.flowStepNumber
複製代碼
您還可使用安全args以類型安全的方式導航,不管是否添加參數。您可使用生成的Directions類來完成此操做
經過操做爲每一個不一樣的目標生成方向類。Directions類包含目標具備的每一個操做的方法。
例如,navigate_action_buttonHomeFragment.kt
中的單擊偵聽器能夠更改成:
HomeFragment.kt
// Note the usage of curly braces since we are defining the click listener lambda
view.findViewById<Button>(R.id.navigate_action_button)?.setOnClickListener{
val flowStepNumberArg = 1
val action = HomeFragmentDirections.nextAction(flowStepNumberArg)
findNavController().navigate(action)
}
複製代碼
請注意,在導航圖XML中,您能夠defaultValue爲每一個參數提供一個。若是你沒有,那麼你必須傳遞參數到動做,如圖所示: HomeFragmentDirections.nextAction(flowStepNumberArg)
導航組件包括一個NavigationUI類和navigation-ui-ktxkotlin擴展。NavigationUI具備將菜單項與導航目的地相關聯的靜態方法,而且navigation-ui-ktx
是一組執行相同操做的擴展函數。若是NavigationUI在當前圖形上找到與目標ID相同的菜單項,則會將菜單項配置爲導航到該目標
使用NavigationUI的最簡單方法之一是簡化選項菜單設置。特別是,NavigationUI簡化了onOptionsItemSelected
回調的處理。
請注意,你怎麼已經有代碼膨脹的菜單overflow_menu
中onCreateOptionsMenu
打開 res/menu/overflow_menu.xml
更新溢出菜單以包含 settings_dest
overflow_menu.xml
<item
android:id="@+id/settings_dest"
android:icon="@drawable/ic_settings"
android:menuCategory="secondary"
android:title="@string/settings" />
複製代碼
打開 MainActivity.kt
onOptionsItemSelected
使用onNavDestinationSelected
輔助方法處理NavigationUI句柄。若是菜單項不是要導航,請使用super.onOptionsItemSelected
MainActivity.kt
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment))
|| super.onOptionsItemSelected(item)
}
複製代碼
代碼已經包含用於實現底部導航的XML佈局代碼,這就是您看到底部導航欄的緣由。但它不會導航到任何地方。
res/layout/navigation_activity/navigation_activity.xml
(h470dp)並單擊「 文本」選項卡請注意底部導航的XML佈局代碼是如何引用的 bottom_nav_menu.xml
navigation_activity.xml(h470dp)
com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/bottom_nav_menu" />
複製代碼
bottom_nav_menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@id/home_dest"
android:icon="@drawable/ic_home"
android:title="@string/home" />
<item
android:id="@id/deeplink_dest"
android:icon="@drawable/ic_android"
android:title="@string/deeplink" />
</menu>
複製代碼
讓底部導航實際上使用NavigationUI作一些事情。
打開 MainActivity.kt
setupBottomNavMenu
使用實現方法setupWithNavController(bottomNavigationView: BottomNavigationView, navController: NavController)
MainActivity.kt
private fun setupBottomNavMenu(navController: NavController) {
val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
bottomNav?.setupWithNavController(navController)
}
複製代碼
最後,讓咱們使用NavigationUI配置側面導航和導航抽屜,包括處理ActionBar和正確的導航。若是您有足夠大的屏幕或者屏幕過短而沒法進行底部導航,您會看到這一點。
首先觀察應用程序中已有適當的佈局XML代碼。
navigation_activity.xml和navigation_activity.xml
(w960dp)請注意兩個佈局如何包含鏈接到nav_drawer_menu
的NavigationView。在平板電腦版本(w960dp)中,NavigationView始終在屏幕上。在較小的設備上,NavigationView 嵌套在DrawerLayout中。
如今開始實施NavigationView導航。
打開 MainActivity.kt
setupNavigationMenu
使用實現方法setupWithNavController(navigationView: NavigationView, navController: NavController)
。請注意該方法的版本如何使用NavigationView而不是a BottomNavigationView。
MainActivity.kt
private fun setupNavigationMenu(navController: NavController) {
val sideNavView = findViewById<NavigationView>(R.id.nav_view)
sideNavView?.setupWithNavController(navController)
}
複製代碼
如今導航視圖菜單將顯示在屏幕上,但不會影響ActionBar。
設置ActionBar須要建立一個實例AppBarConfiguration。目的AppBarConfiguration是爲工具欄,摺疊工具欄和操做欄指定所需的配置選項。配置選項包括欄是否必須處理抽屜佈局以及哪些目的地被視爲頂級目的地。
頂級目標是應用程序的根級目標。這些目的地不會在應用欄中顯示「向上」按鈕,若是目的地使用抽屜佈局,它們會顯示抽屜圖標。
MainActivity.kt
val drawerLayout : DrawerLayout? = findViewById(R.id.drawer_layout)
appBarConfiguration = AppBarConfiguration(
setOf(R.id.home_dest, R.id.deeplink_dest),
drawerLayout)
複製代碼
如何肯定頂級目的地 經過全局導航UI可到達的目的地,例如底部導航或側面導航,全部目標都顯示給用戶,由於它們位於層次結構的同一頂層。所以,它們是頂級目的地。
home_dest而且deeplink_dest
在底部導航中,咱們但願抽屜圖標顯示在這兩個目的地上,所以它們是頂級目的地。 請注意,起始目標始終被視爲頂級目標。若是未指定頂級目標列表,則惟一的頂級目標是您的起始目標。您能夠AppBarConfiguration
在文檔中瞭解更多信息。
5.實施 setupActionBarWithNavController
MainActivity.kt
private fun setupActionBar(navController: NavController,
appBarConfig : AppBarConfiguration) {
setupActionBarWithNavController(navController, appBarConfig)
}
複製代碼
您還應該讓NavigationUI處理按下向上按鈕時發生的狀況。
onSupportNavigationUp
並NavigationUI.navigateUp
使用相同的呼叫AppBarConfiguration
。MainActivity.kt
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.my_nav_host_fragment).navigateUp(appBarConfiguration)
}
複製代碼
佈局navigation_activity.xml(h470dp)將在縱向模式下在手機上使用。這種佈局也未包含該導航抽屜,而是包括底部導航,這就是爲何你要在分屏打開應用程序,看看抽屜式導航欄。沒有導航抽屜和底部導航佈局的緣由是由於Material Design準則警告不要這樣作。
添加新目的地NavigationView很容易。導航抽屜使用向上和向後導航後,只需添加新菜單項便可。
menu/nav_drawer_menu.xml
9.添加新菜單項 settings_dest
nav_drawer_menu.xml
<item
android:id="@+id/settings_dest"
android:icon="@drawable/ic_settings"
android:title="@string/settings" />
複製代碼
導航組件還包括深層連接支持。深層連接是一種跳入應用導航中間的方式,不管是來自實際的URL連接仍是來自通知的待處理意圖。
使用導航庫處理深層連接的一個好處是,它能夠確保用戶使用來自其餘入口點(如應用程序小部件,通知或Web連接)的相應後備堆啓動正確的目標(在下一步中介紹)。
導航提供了一個NavDeepLinkBuilder
類來構建一個PendingIntent
將用戶帶到特定目的地的類
咱們將使用將NavDeepLinkBuilderapp
小部件鏈接到目標。
1.打開 DeepLinkAppWidgetProvider.kt
2.添加一個PendingIntent
構造NavDeepLinkBuilder
:
DeepLinkAppWidgetProvider
val args = Bundle()
args.putString("myarg", "From Widget");
val pendingIntent = NavDeepLinkBuilder(context)
.setGraph(R.navigation.mobile_navigation)
.setDestination(R.id.deeplink_dest)
.setArguments(args)
.createPendingIntent()
remoteViews.setOnClickPendingIntent(R.id.deep_link_button, pendingIntent)
複製代碼
注意:
setGraph
包括導航圖。setDestination
指定連接的位置。setArguments
包括您要傳遞到深層連接的任何參數。默認狀況下
NavDeepLinkBuilder
將啓動您的啓動器活動。您能夠經過將活動做爲上下文傳遞或經過設置顯式活動類來覆蓋此行爲setComponentName()
。
爲方便起見,你也能夠調用NavController的createDeepLink()方法使用Context從當前和導航圖NavController。
使用您傳入的導航圖肯定深層連接的後臺堆棧。若是您選擇的顯式活動具備父活動,則還包括這些父活動。
使用指定的目標生成backstack app:startDestination。在這個應用程序中,咱們只有一個活動和一個級別的導航,所以backstack將帶您到home_dest目的地。
更復雜的導航能夠包括嵌套導航圖。的app:startDestination在嵌套圖的每一個水平決定返回堆棧。有關深層連接和嵌套圖形的更多信息,請查看導航原理。
深層連接最多見的用途之一是容許Web連接在您的應用中打開活動。傳統上,您將使用intent-filter並將URL與您要打開的活動相關聯。
導航庫使這很是簡單,並容許您將URL直接映射到導航圖中的目標。
是您能夠添加到圖表中目標的元素。每一個元素都有一個必需的屬性:app:uri。
除了直接URI匹配外,還支持如下功能:
在此步驟中,您將添加指向www.example.com的深層連接。
打開 mobile_navigation.xml
將元素添加到deeplink_dest目標。
mobile_navigation.xml
<fragment
android:id="@+id/deeplink_dest"
android:name="com.example.android.codelabs.navigation.DeepLinkFragment"
android:label="@string/deeplink"
tools:layout="@layout/deeplink_fragment">
<argument
android:name="myarg"
android:defaultValue="Android!"/>
<deepLink app:uri="www.example.com/{myarg}" />
</fragment>
複製代碼
打開 AndroidManifest.xml
添加nav-graph標籤。這將確保生成適當的intent過濾器
AndroidManifest.xml中
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<nav-graph android:value="@navigation/mobile_navigation" />
</activity>
複製代碼
若是您想知道生成了什麼,能夠在輸出APK中找到結果。 在項目視圖中,導航到app - > build - > outputs - > apk - > debug - > app-debug.apk 雙擊app-debug.apk以在APK Analyzer中打開。在這裏,您將可以看到生成的AndroidManifest。
adb shell am start -a android.intent.action.VIEW -d「http://www.example.com/urlTest」