6 月 3 日の「#Android11: The Beta Launch Show」にぜひご参加ください。

NavigationUI を使用して UI コンポーネントを更新する

Navigation Architecture コンポーネントには、NavigationUI クラスがあります。このクラスには、トップ アプリバーやナビゲーション ドロワー、ボトム ナビゲーションを使用してナビゲーションを管理する静的メソッドが含まれています。

ナビゲーション イベントをリッスンする

デスティネーション間のナビゲーションは、主に NavController とのインタラクションによって実行されます。NavController は、NavHost のコンテンツを新しいデスティネーションに置き換える役割を担います。多くの場合、UI 要素(トップ アプリバーや、BottomNavigationBar のような永続的ナビゲーション コントロールなど)は、NavHost の外側に存在しており、デスティネーション間を移動する際に更新する必要があります。

NavController は、 の現在のデスティネーションまたはその引数が変更されたときに呼び出される OnDestinationChangedListener インターフェースを提供します。新しいリスナーは、addOnDestinationChangedListener() メソッドを通じて登録できます。addOnDestinationChangedListener() を呼び出したときに、現在のデスティネーションが存在した場合、すぐにリスナーに送信されます。

NavigationUI は、OnDestinationChangedListener を使用して、このような一般的な UI コンポーネントにナビゲーション認識機能を追加します。また、OnDestinationChangedListener を単独で使用して、カスタム UI やカスタム ビジネス ロジックにナビゲーション イベント認識機能を追加することもできます。

たとえば、アプリの一部の領域では表示し、他の領域では非表示にする一般的 UI 要素があるとします。独自の OnDestinationChangedListener を使用することで、ターゲット デスティネーションに基づいてこの UI 要素の表示 / 非表示を選択的に指定することができます。次の例をご覧ください。

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 を使用してツールバーを作成するには、まず、メイン アクティビティ内でバーを定義します。以下をご覧ください。

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

次に、メイン アクティビティの 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 を組み込むには、まず、メイン アクティビティ内でツールバーと周囲のレイアウトを定義します。以下をご覧ください。

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

次に、メイン アクティビティの 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);
    }
    

アクションバー

デフォルト アクションバーにナビゲーション サポートを追加するには、メイン アクティビティの onCreate() メソッドから setupActionBarWithNavController() を呼び出します。以下をご覧ください。AppBarConfiguration は、onSupportNavigateUp() をオーバーライドする際にも使用するため、onCreate() の外部で宣言する必要があります。

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 は、デスティネーションをメニュー方式の UI コンポーネントに結び付けるヘルパーも提供します。NavigationUI には onNavDestinationSelected() ヘルパー メソッドが含まれており、このヘルパー メソッドは、MenuItem と、関連付けられたデスティネーションをホストする NavController を受け取ります。MenuItemid がデスティネーションの id と一致すると、NavController は、そのデスティネーションへのナビゲーションを実行します。

たとえば、以下の XML スニペットの場合、メニュー項目とデスティネーションを定義する際、共通の id である details_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>
    

たとえば、アクティビティの onCreateOptionsMenu() を通じてメニューを追加した場合、アクティビティの 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 を持つデスティネーションに自動的に移動するようになります。

ナビゲーション ドロワーを追加する

ナビゲーション ドロワーは、アプリのメイン ナビゲーション メニューを表示する UI パネルです。ドロワーは、ユーザーがアプリバー内のドロワー アイコン()をタップしたときや、画面の左端からスワイプしたときに表示されます。

ドロワー アイコンは、DrawerLayout を使用しているすべてのトップレベル デスティネーション上で表示されます。トップレベル デスティネーションは、アプリのルートレベル デスティネーションです。トップレベル デスティネーションの場合、アプリバーに [上へ] ボタンは表示されません。

ナビゲーション ドロワーを追加するには、まず、DrawerLayout をルートビューとして宣言します。DrawerLayout 内に、メイン UI コンテンツのレイアウトと、ナビゲーション ドロワーのコンテンツを格納する別のビューを追加します。

たとえば、下記のレイアウトの場合、2 つの子ビューを持つ 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>
    

次に、AppBarConfiguration に渡すことによって、DrawerLayout をナビゲーション グラフに接続します。以下をご覧ください。

Kotlin

    val appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)
    

Java

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

次に、メイン アクティビティ クラス内で、メイン アクティビティの 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() を呼び出して、ボトム ナビゲーション バー内で選択されているアイテムを自動的に更新します。

アプリ内にボトム ナビゲーション バーを作成するには、まず、メイン アクティビティ内でバーを定義します。以下をご覧ください。

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

次に、メイン アクティビティ クラス内で、メイン アクティビティの 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 Architecture Components 高機能ナビゲーション サンプルをご覧ください。

参考リンク

ナビゲーションについて詳しくは、以下の参考情報をご確認ください。

サンプル

コードラボ

ブログ記事

動画