目录
一、前言
通过NavigationUI可以将导航组件与UI关联起来,在导航时自动更新UI。
二、绑定AppBar
通过NavigationUI绑定AppBar之后,导航时可以自动更新AppBar的内容。例如标题会显示我们在目的地里配置的android:label
属性内容。
1.ActionBar
对于使用了含有ActionBar主题的Activity,我们可以通过NavigationUI的setupActionBarWithNavController()
方法绑定ActionBar:
// 第一个参数是Activity,第二个是NavController
NavigationUI.setupActionBarWithNavController(this, navController);
2.Toolbar(推荐)
对于没有使用含有ActionBar主题的Activity,我们可以通过NavigationUI的setupWithNavController()
方法绑定Toolbar。
// 第一个参数是Toolbar,第二个是NavController
NavigationUI.setupWithNavController(toolbar, navController);
该Toolbar在Activity的布局文件中,格式为:
<LinearLayout>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
... />
...
</LinearLayout>
基于Toolbar可定制化程度高、应用场景多等优点,谷歌推荐使用Toolbar取代/作为ActionBar。
补充:
- 如果Fragment需要使用Activity的AppBar,那么应该在Activity调用
setSupportActionBar(toolbar)
方法。
即使这样会导致第一个目的地标题显示的是app name而不是自己的label。 - 关于Fragment使用AppBar的细节,参考官方文档
三、AppBarConfiguration解释
通过AppBarConfiguration可以设置AppBar的一些特殊属性,例如:
// Builder构造方法里为top-level destinations。top-level destinations的AppBar没有返回箭头。
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(R.id.cFragment)
.setFallbackOnNavigateUpListener(new AppBarConfiguration.OnNavigateUpListener() {
// 给AppBar的返回键设置备用监听事件。NavController.navigateUp()返回false时触发,例如处于start destination时NavController.navigateUp()返回false
public boolean onNavigateUp() {
// 自定义AppBar的返回箭头事件
// 例如可以为 requireActivity().onBackPressed();
return true;
}
}).build();
NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration);
补充:如果没手动设置AppBarConfiguration
,那么start destination默认是top-level destination。
四、Menu导航
如果MenuItem的id与目的地id一致,那么点击MenuItem时就可以使用NavigationUI.onNavDestinationSelected()
方法实现导航。
假设导航图为
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"
app:startDestination="@id/aFragment">
<fragment
android:id="@+id/aFragment"
android:name="com.scx.navigation.navigationui.AFragment"
android:label="aFragment"/>
<fragment
android:id="@+id/bFragment"
android:name="com.scx.navigation.navigationui.BFragment"
android:label="bFragment{argName}" >
<argument
android:name="argName"
app:argType="string"
android:defaultValue="(arg)" />
</fragment>
<fragment
android:id="@+id/cFragment"
android:name="com.scx.navigation.navigationui.CFragment"
android:label="CFragment" />
<fragment
android:id="@+id/dFragment"
android:name="com.scx.navigation.navigationui.DFragment"
android:label="DFragment" />
</navigation>
那么menu就应该为
menu.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<!-- WARN: Item Id Must AS Same As Destination ID in nav_graph.xml-->
<item
android:id="@+id/aFragment"
android:title="A" />
<item
android:id="@+id/bFragment"
android:title="B" />
<item
android:id="@+id/cFragment"
android:title="C" />
<item
android:id="@+id/dFragment"
android:title="D" />
</menu>
只需在MenuItem的点击事件里调用NavigationUI.onNavDestinationSelected()方法即可实现导航:
toolbar.setOnMenuItemClickListener(item -> {
NavigationUI.onNavDestinationSelected(item, navController);
return true;
});
五、BottomNavigationView导航
通过NavigationUI.setupWithNavController()
方法绑定BottomNavigationView实现BottomNavigationView导航。
- 在Activity的布局文件中添加BottomNavigationView
......
<!-- BottomNavigationView的menu为menu.xml -->
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="@menu/menu" />
......
- 绑定BottomNavigationView
BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_nav);
NavigationUI.setupWithNavController(bottomNavigationView, navController);
说明:
- BottomNavigationView本质上就是Menu导航
- 在导航时默认保存和恢复目的地的状态
- 一般将BottomNavigationView对应的目的地设为top-level destinations
六、Navigation Drawer导航
本质上还是Menu导航,只不过在UI表现和使用方法上有所不同。
- 使用DrawerLayout + NavigationView
<?xml version="1.0" encoding="utf-8"?><!-- Use DrawerLayout as root container for activity -->
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<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="wrap_content"
android:background="@color/design_default_color_primary"
android:elevation="4dp"
android:theme="@style/ThemeOverlay.Toolbar"
app:contentInsetStartWithNavigation="0dp"
app:layout_constraintTop_toTopOf="parent"
app:titleTextColor="@color/white" />
<!-- Layout to contain contents of main body of screen (drawer will slide over this) -->
<androidx.fragment.app.FragmentContainerView
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/nav_graph" />
</LinearLayout>
<!-- Container for contents of drawer - use NavigationView to make configuration easier -->
<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/header"
app:menu="@menu/menu" />
</androidx.drawerlayout.widget.DrawerLayout>
- 绑定DrawerLayout和NavigationView
// 绑定DrawerLayout
DrawerLayout drawerLayout = findViewById(R.id.drawer_layout);
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(R.id.aFragment)
.setOpenableLayout(drawerLayout)
.build();
NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration);
// 绑定NavigationView
NavigationView navigationView = findViewById(R.id.nav_view);
NavigationUI.setupWithNavController(navigationView, navController);
说明:top-level destinations的AppBar显示drawer icon
七、OnDestinationChangedListener
通过OnDestinationChangedListener
监听导航事件。
使用方法:
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
@Override
public void onDestinationChanged(@NonNull NavController controller,
@NonNull NavDestination destination, @Nullable Bundle arguments) {
//destination为当前destination,arguments为传递的参数
}
});
八、效果与代码
1.效果
Menu、BottomNavigationView导航:
Navigation Drawer导航:
2.代码
ps:在AndroidManifest.xml中切换主Activity查看Drawer和其他类型的效果。