Navigation 组件包含 NavigationUI
类。此类包含使用顶部应用栏、抽屉式导航栏和底部导航栏管理导航的静态方法。
可参阅:
Android Material 常用组件详解(七)—— BottomNavigationView 使用详解
Android Material 常用组件详解(九)—— NavigationView 使用详解
Android Material 常用组件详解(十)—— ToolBar、AppBarLayout、CoordinatorLayout、CollapsingToolbarLayout 使用详解
顶部应用栏在应用顶部提供了一个固定位置,用于显示当前屏幕的信息和操作。如需详细了解应用栏,请参阅设置应用栏。
NavigationUI
包含在用户浏览您的应用时自动更新顶部应用栏中内容的方法。例如,NavigationUI 使用导航图中的目的地标签android:label
及时更新顶部应用栏的标题,即应用栏的标题会显示与之对应的android:label
内容。
<navigation>
<fragment ...
android:label="Page title">
...
fragment>
navigation>
NavigationUI 支持以下顶部应用栏类型:
NavigationUI 使用 AppBarConfiguration
对象管理在应用显示区域左上角的导航按钮行为。导航按钮的行为会根据用户是否位于顶级目的地而变化。
当用户位于顶级目的地时,如果目的地使用 DrawerLayout,导航按钮会变为抽屉式导航栏图标 。如果目的地没有使用 DrawerLayout,导航按钮处于隐藏状态。当用户位于任何其他目的地上时,导航按钮会显示为向上按钮 。
<LinearLayout>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" />
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
... />
...
LinearLayout>
val navController = findNavController(R.id.nav_host_fragment)
val appBarConfiguration = AppBarConfiguration(navController.graph)
// val appBarConfiguration = AppBarConfiguration(setOf( R.id.fragmentA, R.id.fragmentB, R.id.fragmentC))
toolbar.setupWithNavController(navController, appBarConfiguration)
<LinearLayout>
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="@dimen/tall_toolbar_height">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleGravity="top"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"/>
com.google.android.material.appbar.CollapsingToolbarLayout>
com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
... />
...
LinearLayout>
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.activity_main)
val layout = findViewById<CollapsingToolbarLayout>(R.id.collapsing_toolbar_layout)
val toolbar = findViewById<Toolbar>(R.id.toolbar)
val navController = findNavController(R.id.nav_host_fragment)
val appBarConfiguration = AppBarConfiguration(navController.graph)
layout.setupWithNavController(toolbar, navController, appBarConfiguration)
}
如需向默认操作栏添加导航支持,请通过主 Activity 的 onCreate()
方法调用 setupActionBarWithNavController()
,如下所示。请注意,您需要在 onCreate() 之外声明 AppBarConfiguration,因为您在替换 onSupportNavigateUp() 时也使用该方法:
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
val navController = findNavController(R.id.nav_host_fragment)
appBarConfiguration = AppBarConfiguration(navController.graph)
setupActionBarWithNavController(navController, appBarConfiguration)
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
抽屉式导航栏是显示应用主导航菜单的界面面板。当用户触摸应用栏中的抽屉式导航栏图标 或用户从屏幕的左边缘滑动手指时,就会显示抽屉式导航栏。
XML布局
<androidx.drawerlayout.widget.DrawerLayout 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:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary" />
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation" />
LinearLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer" />
androidx.drawerlayout.widget.DrawerLayout>
NavigationView的菜单每个选项的id必须要和导航图中的每个fragment的id相同,这样才能当用户点击菜单项时,应用会自动使用相同的 id 导航到相应目的地。
<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"
android:id="@+id/mobile_navigation"
app:startDestination="@+id/nav_home">
<fragment
android:id="@+id/nav_home"
android:name="com.matt.myapplication3.ui.home.HomeFragment"
android:label="@string/menu_home"
tools:layout="@layout/fragment_home" />
<fragment
android:id="@+id/nav_gallery"
android:name="com.matt.myapplication3.ui.gallery.GalleryFragment"
android:label="@string/menu_gallery"
tools:layout="@layout/fragment_gallery" />
<fragment
android:id="@+id/nav_slideshow"
android:name="com.matt.myapplication3.ui.slideshow.SlideshowFragment"
android:label="@string/menu_slideshow"
tools:layout="@layout/fragment_slideshow" />
navigation>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="navigation_view">
<group android:checkableBehavior="single">
<item
android:id="@+id/nav_home"
android:icon="@drawable/ic_menu_camera"
android:title="@string/menu_home" />
<item
android:id="@+id/nav_gallery"
android:icon="@drawable/ic_menu_gallery"
android:title="@string/menu_gallery" />
<item
android:id="@+id/nav_slideshow"
android:icon="@drawable/ic_menu_slideshow"
android:title="@string/menu_slideshow" />
group>
menu>
将抽屉菜单与导航组件、应用栏关联
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 将自定义的toolbar取代原本的actionbar
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// 将导航组件与应用栏关联
// appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)
// appBarConfiguration = AppBarConfiguration(navView.menu, drawerLayout)
appBarConfiguration = AppBarConfiguration(setOf(
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow), drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
// 将抽屉式导航栏与导航组件
navView.setupWithNavController(navController)
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}
setSupportActionBar
setSupportActionBar
是用自定义的Toolbar替换原本的actionbar,在替换之后才能使用actionbar的方法,例如,上面的示例中,在调用 setupActionBarWithNavController()
方法自动修改Toolbar的标题为导航图中的目的地标签android:label
AppBarConfiguration
AppBarConfiguration
有3种创建方式
@Suppress("FunctionName") /* Acts like a constructor */
inline fun AppBarConfiguration(
navGraph: NavGraph,
drawerLayout: Openable? = null,
noinline fallbackOnNavigateUpListener: () -> Boolean = { false }
) = AppBarConfiguration.Builder(navGraph)
.setOpenableLayout(drawerLayout)
.setFallbackOnNavigateUpListener(fallbackOnNavigateUpListener)
.build()
@Suppress("FunctionName") /* Acts like a constructor */
inline fun AppBarConfiguration(
topLevelMenu: Menu,
drawerLayout: Openable? = null,
noinline fallbackOnNavigateUpListener: () -> Boolean = { false }
) = AppBarConfiguration.Builder(topLevelMenu)
.setOpenableLayout(drawerLayout)
.setFallbackOnNavigateUpListener(fallbackOnNavigateUpListener)
.build()
@Suppress("FunctionName") /* Acts like a constructor */
inline fun AppBarConfiguration(
topLevelDestinationIds: Set<Int>,
drawerLayout: Openable? = null,
noinline fallbackOnNavigateUpListener: () -> Boolean = { false }
) = AppBarConfiguration.Builder(topLevelDestinationIds)
.setOpenableLayout(drawerLayout)
.setFallbackOnNavigateUpListener(fallbackOnNavigateUpListener)
.build()
第一种和第二种方式的效果相同,在切换fragment之后,图标 自动变成 。
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/bottom_nav_menu" />
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@id/nav_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/mobile_navigation" />
androidx.constraintlayout.widget.ConstraintLayout>
BottomNavigationView的菜单每个选项的id必须要和导航图中的每个fragment的id相同,这样才能当用户点击菜单项时,应用会自动使用相同的 id 导航到相应目的地。和抽屉式导航栏一致,这里不再说明
将底部导航与导航组件、应用栏关联
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
val appBarConfiguration = AppBarConfiguration(setOf(
R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications))
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
}
在 Kotlin 中,您可以让共享数据的Fragment使用 navGraphViewModels()
属性委托从 Fragment 目标中检索 ViewModel,创建一个范围限定于导航图的 ViewModel,从而使您能够在图表的目标间共享与界面相关的数据。可以以这种方式创建的任何 ViewModel 对象都会一直存在,直至关联的 NavHost
及其 ViewModelStore
遭到清除,或导航图从返回栈中弹出为止。
ShareDataModel .kt
class ShareDataModel : ViewModel() {
var share: String? = null
}
BlankFragment.kt
class BlankFragment : Fragment() {
private val shareDataModel by navGraphViewModels<ShareDataModel>(R.id.nav_graph)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
shareDataModel.share = "share data from navGraphViewModels"
val binding = FragmentBlankBinding.inflate(inflater, container, false)
binding.button.setOnClickListener {
findNavController().navigate(BlankFragmentDirections.actionBlankFragmentToBlankFragment2())
}
return binding.root
}
}
BlankFragment2.kt
class BlankFragment2 : Fragment() {
private val shareDataModel by navGraphViewModels<ShareDataModel>(R.id.nav_graph)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentBlank2Binding.inflate(inflater, container, false)
binding.textview.text = shareDataModel.share
return binding.root
}
}