Android 开发之深入浅出 NavigationUI,2020-2021 阿里巴巴安卓面试真题解析
在之前的?导航系列文章中,Chet?开发了一个用于?跟踪甜甜圈的应用。知道什么是甜甜圈的最佳搭档吗?(难道是另一个甜甜圈?) 当然是咖啡!所以我准备增加一个追踪咖啡的功能。我需要在应用中增加一些页面,所以有必要使用抽屉式导航栏或者底部标签栏来辅助用户导航。但是我们该如何使用这些 UI 组件来集成导航功能呢?通过点击监听器手动触发导航动作吗?
不需要!无需任何监听器。NavigationUI 类通过匹配目标页面 id 与菜单 id 实现不同页面之间的导航功能。让我们深入探索一下它的内部机制吧。
添加咖啡追踪器
△ 工程结构
首先我将与甜甜圈相关的类文件拷贝了一份到新的包下,并且将它们重命名。这样的操作对于真正的应用来说也许不是最好的做法,但是在这里可以快速帮助我们添加咖啡跟踪功能到已有的应用中。如果您希望随着文章内容同步操作,可以获取?这里的代码,里面包含了全部针对 Donut Tracker 应用的修改,可以基于该代码了解 NavigationUI。
基于上面所做的修改,我更新了导航图,新增了从 coffeeFragment 到 coffeeDialogFragment 以及从 selectionFragment 到 donutFragment 相关的目的页面和操作。之后我会用到这些目的页面的 id ;)
△ 带有新的目的页面的导航图
更新导航图之后,我们可以开始将元素绑定起来,并且实现导航到 SelectionFragment。
选项菜单
应用的选项菜单现在尚未发挥作用。要启用它,需要在?onOptionsItemSelected()
?函数中,为被选择的菜单项调用?onNavDestinationSelected()
?函数,并传入?navController
。只要目的页面的?id
?和?MenuItem
?的 id 相匹配,该函数会导航到绑定在?MenuItem
?上的目的页面。
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return item.onNavDestinationSelected(
findNavController(R.id.nav_host_fragment)
) || super.onOptionsItemSelected(item)
}
现在导航控制器可以 "支配" 菜单项了,我将?MenuItem
?的?id
?与之前所创建的目的页面的?id
?进行了匹配。这样,导航组件就可以将?MenuItem
?与目的页面进行关联。
<menu 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"
tools:context="com.android.samples.donuttracker.MainActivity">
<item
android:id="@+id/selectionFragment"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="never" />
</menu>
Toolbar
现在应用可以
导航到?selectionFragment
,但是标题仍然保持原样。当处于?selectionFragment
?的时候,我们希望标题可以被更新并且显示返回按钮。
首先我需要添加一个?AppBarConfiguration
?对象,NavigationUI
?会使用该对象来管理应用左上角的导航按钮的行为。
appBarConfiguration = AppBarConfiguration(navController.graph)
该按钮会根据您的目的页面的层级改变自身的行为。比如,当您在最顶层的目的页面时,就不会显示回退按钮,因为没有更高层级的页面。
默认情况下,您应用的最初页面是唯一的最顶层目的页面,但是您也可以定义多个最顶层目的页面。比如,在我们的应用中,我可以将?donutList?和?coffeeList?的目的页面都定义为最顶层的目的页面。
接下来,在?MainActivity
?类中,获得?navController
?和?toolbar
?的实例,并且验证?setSupportActionBar()
?是否被调用。这里我还更新了传入函数的 toolbar 的引用。
val navHostFragment = supportFragmentManager.findFragmentById(
R.id.nav_host_fragment
) as NavHostFragment
navController = navHostFragment.navController
val toolbar = binding.toolbar
要在默认的操作栏 (Action Bar) 中添加导航功能,我在这里使用了?setupActionBarWithNavController()
?函数。该函数需要两个参数:?navController
?和?appBarConfiguration
。
setSupportActionBar(toolbar)
setupActionBarWithNavController(navController, appBarConfiguration)
接下来,根据目前的目的页面,我覆写了?onSupportNavigationUp()
?函数,然后在?nav_host_fragment
?上调用?navigateUp()
?并传入?appBarConfiguration
?来支持回退导航或者显示菜单图标的功能。
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.nav_host_fragment).navigateUp(
appBarConfiguration
)
}
现在我可以导航到 selectionFragment,并且您可以看到标题已经更新,并且也显示了返回按钮,用户可以返回到之前的页面。
△ 标题更新了并且也显示了返回按钮
底部标签栏
目前为止还算顺利,但是应用还不能导航到?coffeeList
?Fragment。接下来我们将解决这个问题。
我们从添加底部标签栏入手。首先添加?bottom_nav_menu.xml
?文件并且声明两个菜单元素。NavigationUI
?依赖?MenuItem
?的?id
,用它与导航图中目的页面的 id 进行匹配。我还为每个目的页面设置了图标和标题。
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@id/donutList"
android:icon="@drawable/donut_with_sprinkles"
android:title="@string/donut_name" />
<item
android:id="@id/coffeeList"
android:icon="@drawable/coffee_cup"
android:title="@string/coffee_name" />
</menu>
现在?MenuItem
?已经就绪,我在?mainActivity
?的布局中添加了?BottomNavigationView
,并且将?bottom_nav_menu
?设置为?BottomNavigationView
的?menu
?属性。
<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" />
要使底部标签栏发挥作用,这里调用?setupWithNavController()
?函数将?navController
?传入?BottomNavigationView
。
private fun setupBottomNavMenu(navController: NavController) {
val bottomNav = findViewById<BottomNavigationView>(
R.id.bottom_nav_view
)
bottomNav?.setupWithNavController(navController)
评论