欢迎参加我们将于 6 月 3 日举行的 #Android11:Beta 版发布会

使用 NavigationUI 更新界面组件

导航架构组件包含 NavigationUI 类。此类包含使用顶部应用栏、抽屉式导航栏和底部导航栏管理导航的静态方法。

监听导航事件

NavController 进行互动是在不同目的地之间导航的主要方法。NavController 负责将 NavHost 的内容替换为新目的地。在大多数情况下,界面元素(如顶部应用栏或 BottomNavigationBar 等其他持续性导航控件)位于 NavHost 之外,并且随您在各个目的地之间导航进行更新。

NavController 提供 OnDestinationChangedListener 接口,该接口在 的当前目的地或其参数发生更改时调用。可以通过 addOnDestinationChangedListener() 方法注册新监听器。请注意,调用 addOnDestinationChangedListener() 时,如果当前目的地存在,则会被立即发送到您的监听器。

NavigationUI 使用 OnDestinationChangedListener 让这些常见界面组件具备导航感知功能。不过请注意,您也可以单独使用 OnDestinationChangedListener,使任何自定义界面或业务逻辑感知导航事件。

举例来说,您可能会在应用的一些区域显示常见界面元素,而在另外一些区域隐藏这些元素。使用您自己的 OnDestinationChangedListener,您可以根据目标目的地选择性地显示或隐藏这些界面元素,如下例所示:

Kotlin

    navController.addOnDestinationChangedListener { _, destination, _ ->
       if(destination.id == R.id.full_screen_destination) {
           toolbar.visibility = View.GONE
           bottomNavigationView.visibility = View.GONE
       } else {
           toolbar.visibility = View.VISIBLE
           bottomNavigationView.visibility = View.VISIBLE
       }
    }
    

Java

    navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
       @Override
       public void onDestinationChanged(@NonNull NavController controller,
               @NonNull NavDestination destination, @Nullable Bundle arguments) {
           if(destination.getId() == R.id.full_screen_destination) {
               toolbar.setVisibility(View.GONE);
               bottomNavigationView.setVisibility(View.GONE);
           } else {
               toolbar.setVisibility(View.VISIBLE);
               bottomNavigationView.setVisibility(View.VISIBLE);
           }
       }
    });
    

顶部应用栏

顶部应用栏在应用顶部提供了一个固定位置,用于显示当前屏幕上的信息和操作。

NavigationUI 包含在用户浏览您的应用时自动更新顶部应用栏中内容的方法。例如, 使用导航图中的目的地标签及时更新顶部应用栏的标题。

针对下面介绍的顶部应用栏方法使用 NavigationUI 时,您附加到目的地的标签可以使用标签中的 {argName} 格式,从提供给相应目的地的参数中自动填充。

NavigationUI 支持以下顶部应用栏类型:

AppBarConfiguration

NavigationUI 使用 AppBarConfiguration 对象管理在应用显示区域左上角的导航按钮行为。默认情况下,如果用户位于导航图的顶级目的地,则导航按钮会隐藏并且在任何其他目的地显示为向上按钮。

要将导航图的起始目的地用作唯一顶级目的地,您可以创建 AppBarConfiguration 对象并传入相应的导航图,如下所示:

Kotlin

    val appBarConfiguration = AppBarConfiguration(navController.graph)
    

Java

    AppBarConfiguration appBarConfiguration =
            new AppBarConfiguration.Builder(navController.getGraph()).build();
    

如果您想自定义哪些目的地被视为顶级目的地,则可以改为将一组目的地 ID 传递给构造函数,如下所示:

Kotlin

    val appBarConfiguration = AppBarConfiguration(setOf(R.id.main, R.id.android))
    

Java

    AppBarConfiguration appBarConfiguration =
            new AppBarConfiguration.Builder(R.id.main, R.id.android).build();
    

创建工具栏

要使用 NavigationUI 创建工具栏,请先在主 Activity 中定义工具栏,如下所示:

    <LinearLayout>
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar" />
        <fragment
            android:id="@+id/nav_host_fragment"
            ... />
        ...
    </LinearLayout>
    

接着,通过主 Activity 的 onCreate() 方法调用 setupWithNavController(),如下所示:

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        setContentView(R.layout.activity_main)

        ...

        val navController = findNavController(R.id.nav_host_fragment)
        val appBarConfiguration = AppBarConfiguration(navController.graph)
        findViewById<Toolbar>(R.id.toolbar)
            .setupWithNavController(navController, appBarConfiguration)
    }
    

Java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);

        ...

        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        AppBarConfiguration appBarConfiguration =
                new AppBarConfiguration.Builder(navController.getGraph()).build();
        Toolbar toolbar = findViewById(R.id.toolbar);
        NavigationUI.setupWithNavController(toolbar, navController);
    }
    

包含 CollapsingToolbarLayout

要在工具栏中添加 CollapsingToolbarLayout,请先在主 Activity 中定义工具栏和周围布局,如下所示:

    <LinearLayout>
        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="@dimen/tall_toolbar_height">

            <android.support.design.widget.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">

                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    app:layout_collapseMode="pin"/>
            </android.support.design.widget.CollapsingToolbarLayout>
        </android.support.design.widget.AppBarLayout>

        <fragment
            android:id="@+id/nav_host_fragment"
            ... />
        ...
    </LinearLayout>
    

接着,通过主 Activity 的 onCreate 方法调用 setupWithNavController(),如下所示:

Kotlin

    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)
    }
    

Java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);

        ...

        CollapsingToolbarLayout layout = findViewById(R.id.collapsing_toolbar_layout);
        Toolbar toolbar = findViewById(R.id.toolbar);
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        AppBarConfiguration appBarConfiguration =
                new AppBarConfiguration.Builder(navController.getGraph()).build();
        NavigationUI.setupWithNavController(layout, toolbar, navController, appBarConfiguration);
    }
    

操作栏

要向默认操作栏添加导航支持,请通过主 Activity 的 onCreate() 方法调用 setupActionBarWithNavController(),如下所示。请注意,您需要在 onCreate() 之外声明 AppBarConfiguration,因为您在替换 onSupportNavigateUp() 时也使用该方法:

Kotlin

    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)
    }
    

Java

    AppBarConfiguration appBarConfiguration;

    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...

        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
    }
    

接着,替换 onSupportNavigateUp() 以处理向上导航:

Kotlin

    override fun onSupportNavigateUp(): Boolean {
        val navController = findNavController(R.id.nav_host_fragment)
        return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
    }
    

Java

    @Override
    public boolean onSupportNavigateUp() {
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        return NavigationUI.navigateUp(navController, appBarConfiguration)
                || super.onSupportNavigateUp();
    }
    

将目的地关联到菜单项

NavigationUI 还提供辅助程序,用于将目的地关联到菜单驱动的界面组件。NavigationUI 包含一个辅助程序方法 onNavDestinationSelected(),该方法使用 MenuItem 以及托管关联目的地的 NavController。如果 MenuItemid 与目的地的 id 匹配,则 NavController 可以导航至该目的地。

例如,下面的 XML 代码段使用常见的 iddetails_page_fragment 定义菜单项及目的地:

    <?xml version="1.0" encoding="utf-8"?>
    <navigation xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:android="http://schemas.android.com/apk/res/android"
        ... >

        ...

        <fragment android:id="@+id/details_page_fragment"
             android:label="@string/details"
             android:name="com.example.android.myapp.DetailsFragment" />
    </navigation>
    
    <menu xmlns:android="http://schemas.android.com/apk/res/android">

        ...

        <item
            android:id="@id/details_page_fragment"
            android:icon="@drawable/ic_details"
            android:title="@string/details" />
    </menu>
    

例如,如果通过 Activity 的 onCreateOptionsMenu() 添加菜单,则您可以通过替换 Activity 的 onOptionsItemSelected() 以调用 onNavDestinationSelected() 来将菜单项与目的地相关联,如下所示:

Kotlin

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        val navController = findNavController(R.id.nav_host_fragment)
        return item.onNavDestinationSelected(navController) || super.onOptionsItemSelected(item)
    }
    

Java

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        return NavigationUI.onNavDestinationSelected(item, navController)
                || super.onOptionsItemSelected(item);
    }
    

现在,当用户点击 details_page_fragment 菜单项时,应用会自动使用相同的 id 导航到相应目的地。

添加抽屉式导航栏

抽屉式导航栏是显示应用主导航菜单的界面面板。当用户触摸应用栏中的抽屉式导航栏图标 或用户从屏幕的左边缘滑动手指时,就会显示抽屉式导航栏。

抽屉式导航栏图标会显示在使用 DrawerLayout 的所有顶级目的地上。顶级目的地是应用的根级目的地。它们不会在应用栏中显示向上按钮。

要添加抽屉式导航栏,请先声明 DrawerLayout 为根视图。在 DrawerLayout 内,为主界面内容以及包含抽屉式导航栏内容的其他视图添加布局。

例如,以下布局使用含有两个子视图的 DrawerLayout:包含主内容的 NavHostFragment 和适用于抽屉式导航栏内容的 NavigationView

<?xml version="1.0" encoding="utf-8"?>
    <!-- Use DrawerLayout as root container for activity -->
    <android.support.v4.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">

        <!-- Layout to contain contents of main body of screen (drawer will slide over this) -->
        <fragment
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:id="@+id/nav_host_fragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:navGraph="@navigation/nav_graph" />

        <!-- Container for contents of drawer - use NavigationView to make configuration easier -->
        <android.support.design.widget.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:fitsSystemWindows="true" />

    </android.support.v4.widget.DrawerLayout>
    

接着,将 DrawerLayout 连接到您的导航图,具体方法是将其传递给 AppBarConfiguration,如下所示:

Kotlin

    val appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)
    

Java

    AppBarConfiguration appBarConfiguration =
            new AppBarConfiguration.Builder(navController.getGraph())
                .setDrawerLayout(drawerLayout)
                .build();
    

接着,在您的主 Activity 类中,通过主 Activity 的 onCreate() 方法调用 setupWithNavController(),如下所示:

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        setContentView(R.layout.activity_main)

        ...

        val navController = findNavController(R.id.nav_host_fragment)
        findViewById<NavigationView>(R.id.nav_view)
            .setupWithNavController(navController)
    }
    

Java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);

        ...

        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        NavigationView navView = findViewById(R.id.nav_view);
        NavigationUI.setupWithNavController(navView, navController);
    }
    

底部导航栏

NavigationUI 也可以处理底部导航。当用户选择某个菜单项时,NavController 会调用 onNavDestinationSelected() 并自动更新底部导航栏中的所选项目。

要在应用中创建底部导航栏,请先在主 Activity 中定义底部导航栏,如下所示:

    <LinearLayout>
        ...
        <fragment
            android:id="@+id/nav_host_fragment"
            ... />
        <android.support.design.widget.BottomNavigationView
            android:id="@+id/bottom_nav"
            app:menu="@menu/menu_bottom_nav" />
    </LinearLayout>
    

接着,在您的主 Activity 类中,通过主 Activity 的 onCreate() 方法调用 setupWithNavController(),如下所示:

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        setContentView(R.layout.activity_main)

        ...

        val navController = findNavController(R.id.nav_host_fragment)
        findViewById<BottomNavigationView>(R.id.bottom_nav)
            .setupWithNavController(navController)
    }
    

Java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);

        ...

        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);
        NavigationUI.setupWithNavController(bottomNav, navController);
    }
    

有关包含底部导航栏的综合示例,请参阅 GitHub 上的 Android 架构组件高级导航示例

其他资源

要详细了解 Navigation,请参阅下面列出的其他资源:

示例

Codelab

博文

视频