导航组件是一套用于应用内导航的库,工具和指南。该组件将应用程序的所有导航信息集中在导航图中,为实现从简单按钮点击到复杂导航UI模式的所有操作提供了强大的框架。
Android Studio 3.3包含导航编辑器,可以显示导航图。其他功能和优点包括:
要了解有关导航的更多信息,请查看:
下载代码:
$ git clone https://github.com/googlecodelabs/android-navigation
安装Android Studio 3.2或者更高版本
导航简述:
Android Studio 3.2之后的版本支持导航编辑器,需要先升级到此版本之上。
导航图是一种新的资源文件,可以在其中定义应用中所有可能的路径,并且以可视化的方式展现。![2e3dff480efde8d5](/Users/journey/OneDrive - Premium/我的文章/assets/2e3dff480efde8d5.png)
首先你需要在activity layouts中添加一个组件 NavHostFragment。
<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"
andapp:defaultNavHost="true"
会将系统的返回键关联到 NavHostFragment
app:navGraph="@navigation/mobile_navigation"
声明了 NavHostFragment
的导航图NavHostFragment
中使用一个特殊的类NavController
作为导航控制器。
// Command to navigate to flow_step_one_dest
findNavController().navigate(R.id.flow_step_one_dest)
你可以传入destination ID或者action ID作为参数,这些ID必须在导航图XML文件中注册。
可以用以下三种方法获得NavController
:
Fragment.findNavController()
View.findNavController()
Activity.findNavController(viewId: Int)
不管使用哪种方法,必须确保fragment, view, view ID是
NavHostFragment
类型,或者是其子类。否则会出现IllegalStateException
通过重写NavOptions
可以改变导航默认的过渡效果。
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)
}
在导航图中,使用带箭头的线条表示action
![82f00bfa471bd0ff](/Users/journey/OneDrive - Premium/我的文章/assets/82f00bfa471bd0ff.png)
使用action进行导航有以下优点:
<fragment
android:id="@+id/flow_step_two_dest"
android:name="com.example.android.codelabs.navigation.FlowStepFragment">
<argument
.../>
<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"
app:popUpTo="@id/home_dest">
action>
fragment>
popUpTo
属性用于制定栈底的导航。
在action标签允许你在XML文件中设置好NavOptions
中的选项。
导航组件有个Gradle插件叫做safe args,可以生成类型安全的简单类型,用作destination和action的传递参数。
safe args可以帮你取代以下代码:
val username = arguments?.getString("usernameKey")
替换为这种写法:
val username = args.username
打开项目的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 {
//...
}
在导航图中添加argument
<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>
在代码中获取参数
val safeArgs: FlowStepFragmentArgs by navArgs()
val flowStepNumber = safeArgs.flowStepNumber
导航组件中包含 NavigationUI 类, navigation-ui-ktx kotlin拓展。NavigationUI
中有许多提供给菜单导航所需的静态方法,如果NavigationUI
在菜单选项中找到了和导航图中相同的destination ID,它会将其导航到所配置的目的地。
使用NavigationUI最简单的方法之一就是用它来进行option menu的配置。NavigationUI能使onOptionsItemSelected
callback变得更加简单。
overflow_menu.xml
<item
android:id="@+id/settings_dest"
android:icon="@drawable/ic_settings"
android:menuCategory="secondary"
android:title="@string/settings" />
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment))
|| super.onOptionsItemSelected(item)
}
在底部导航中配置与导航图中相同的destination ID。
<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>
使用setupWithNavController(bottomNavigationView: BottomNavigationView, navController: NavController)
实现 setupBottomNavMenu
方法 。
private fun setupBottomNavMenu(navController: NavController) {
val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
bottomNav?.setupWithNavController(navController)
}
在目标Activity中,实现setupNavigationMenu
private fun setupNavigationMenu(navController: NavController) {
val sideNavView = findViewById<NavigationView>(R.id.nav_view)
sideNavView?.setupWithNavController(navController)
}
通过AppBarConfiguration
设置 ActionBar
。通过AppBarConfiguration
配置好应用的顶层destination IDs 与drawer layout。
val drawerLayout : DrawerLayout? = findViewById(R.id.drawer_layout)
appBarConfiguration = AppBarConfiguration(
setOf(R.id.home_dest, R.id.deeplink_dest),
drawerLayout)
配置好AppBarConfiguration之后,你可以调用 NavigationUI.setupActionBarWithNavController
,它帮你做了以下事情:
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.my_nav_host_fragment).navigateUp(appBarConfiguration)
}
根据Material Design guidelines,你不应该同时使用抽屉导航与底部导航。如果你在应用的竖屏模式布局下使用了底部导航,建议在分屏模式下使用抽屉导航。这里的分屏模式布局文件为
navigation_activity.xml (w960dp)
。
添加新的导航到NavigationView
也非常简单,你只需要在menu的XML文件中新增一个item就可以了。
<item
android:id="@+id/settings_dest"
android:icon="@drawable/ic_settings"
android:title="@string/settings" />
使用导航库来处理Deep Link的好处之一就是,跳转到正确的页面之后,导航库会帮你找到合适的后退栈。
我们使用 NavDeepLinkBuilder
来连接微件与目的地。
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)
后退栈取决于你导航图中的设置。
后退栈由destinations中声明了 app:startDestination
的属性决定。
在更富在的导航图中, app:startDestination
会决定各个层级的后退栈。
更多内容查看 Principles of Navigation。
传统方式是 intent-filter and associate a URL with the activity,不过导航库会让这一步骤更加简单。
URI匹配支持以下特性:
{placeholder_name}
来匹配更多字符。占位符名称需要与入参相同。 http://www.example.com/users/{id}
will match http://www.example.com/users/4
.*
在导航图中,添加
<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
<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>
生成后的AndroidManifest其实是这样的:
<activity android:name="com.example.android.codelabs.navigation.MainActivity"> <intent-filter> ... intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="http" /> <data android:scheme="https" /> <data android:host="www.example.com" /> <data android:pathPrefix="/" /> intent-filter> activity>