Google は、黒人コミュニティに対する人種平等の促進に取り組んでいます。取り組みを見る

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

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

トップ アプリバー

トップ アプリバーは、アプリの上部に沿って同じ場所に配置され、現在の画面に基づく情報やアクションを表示します。

トップ アプリバーが表示されている画面
図 1. トップ アプリバーが表示されている画面

NavigationUI には、ユーザーがアプリ内を移動したときにトップ アプリバー内のコンテンツを自動的に更新するメソッドが含まれています。たとえば、NavigationUI は、ナビゲーション グラフ内のデスティネーション ラベルを使用して、トップ アプリバーのタイトルを最新の状態に保ちます。

<navigation>
    <fragment ...
              android:label="Page title">
      ...
    </fragment>
</navigation>

NavigationUI を使用して以下で説明するようなトップ アプリバーの実装を行った場合、デスティネーションにアタッチするラベルは、ラベル内で {argName} 形式を使用することで、デスティネーションに提供される引数に基づいて自動的に入力されます。

NavigationUI は、以下のタイプのトップ アプリバーをサポートしています。

アプリバーの詳細については、アプリバーの設定をご覧ください。

AppBarConfiguration

NavigationUI は、AppBarConfiguration オブジェクトを使用して、アプリの表示領域の左上隅にあるナビゲーション ボタンの動作を管理します。ナビゲーション ボタンの動作は、ユーザーが「トップレベル デスティネーション」にいるかどうかで変わります。

トップレベル デスティネーションとは、互いに階層構造としての関係を持つデスティネーションの中で、ルートまたは最上位レベルにあるデスティネーションのことです。トップレベル デスティネーションでは、それよりも上位にデスティネーションがないため、トップ アプリバーに「上へ」ボタンが表示されません。デフォルトでは、アプリの開始デスティネーションがトップレベル デスティネーションになります。

ユーザーがトップレベル デスティネーションにいるとき、デスティネーションで DrawerLayout を使用している場合は、ナビゲーション ボタンがドロワー アイコン になります。デスティネーションで DrawerLayout を使用していない場合は、ナビゲーション ボタンが非表示になります。ユーザーが他のデスティネーションにいるときは、ナビゲーション ボタンが「上へ」ボタン になります。開始デスティネーションのみをトップレベル ナビゲーションとして使用するナビゲーション ボタンを設定するには、対象のナビゲーション グラフを渡して AppBarConfiguration オブジェクトを作成します。以下をご覧ください。

Kotlin

val appBarConfiguration = AppBarConfiguration(navController.graph)

Java

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

場合によっては、デフォルトの開始デスティネーションを使用する代わりに、複数のトップレベル デスティネーションの定義が必要となることもあります。このように、互いに階層構造としての関係がなく、それぞれが別々の関連デスティネーション群を持つような兄弟画面がある場合には、BottomNavigationView を使用するのが一般的です。そのような場合は、コンストラクタにデスティネーション ID のセットを渡します。以下をご覧ください。

Kotlin

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

Java

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

ツールバーを作成する

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

<LinearLayout>
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar" />
    <androidx.fragment.app.FragmentContainerView
        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, appBarConfiguration);
}

CollapsingToolbarLayout を組み込む

ツールバーに CollapsingToolbarLayout を組み込むには、まず、アクティビティ内でツールバーと周囲のレイアウトを定義します。以下をご覧ください。

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

次に、メイン アクティビティの 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 navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val navController = navHostFragment.navController
    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);
    NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
    NavController navController = navHostFragment.getNavController();
    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 navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val navController = navHostFragment.navController
    appBarConfiguration = AppBarConfiguration(navController.graph)
    setupActionBarWithNavController(navController, appBarConfiguration)
}

Java

AppBarConfiguration appBarConfiguration;

...

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

    NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
    NavController navController = navHostFragment.getNavController();
    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();
}

アプリバーの変化をサポートする

アプリ内のデスティネーション間でトップ アプリバーのレイアウトが大幅に変化しない場合は、アクティビティにトップ アプリバーを追加しても問題ありません。しかし、トップ アプリバーがデスティネーション間で大幅に変化する場合は、アクティビティからトップ アプリバーを削除し、各デスティネーション フラグメントでトップ アプリバーを定義することを検討してください。

たとえば、あるデスティネーションでは標準の Toolbar を使用し、別のデスティネーションでは AppBarLayout を使用してタブのある複雑なアプリバーを作成するとします(図 2 を参照)。

2 種類のトップ アプリバー(左は標準の toolbar、右は toolbar とタブがある appbarlayout)
図 2. 2 種類のアプリバー。左が標準の Toolbar。右が Toolbar とタブがある AppBarLayout

この例を NavigationUI を使用してデスティネーション フラグメントに実装するには、まず各フラグメント レイアウトにアプリバーを定義します。1 つ目は、標準ツールバーを使用するデスティネーション フラグメントです。

<LinearLayout>
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        ... />
    ...
</LinearLayout>

2 つ目は、タブ付きのアプリバーを使用するデスティネーション フラグメントです。

<LinearLayout>
    <com.google.android.material.appbar.AppBarLayout
        ... />

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            ... />

        <com.google.android.material.tabs.TabLayout
            ... />

    </com.google.android.material.appbar.AppBarLayout>
    ...
</LinearLayout>

ナビゲーションの構成ロジックは、基本的にこれらのフラグメントでも同じですが、アクティビティから初期化するのではなく、各フラグメントの onViewCreated() メソッドから setupWithNavController() を呼び出す必要があるという点が違います。

Kotlin

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val navController = findNavController()
    val appBarConfiguration = AppBarConfiguration(navController.graph)

    view.findViewById<Toolbar>(R.id.toolbar)
            .setupWithNavController(navController, appBarConfiguration)
}

Java

@Override
public void onViewCreated(@NonNull View view,
                          @Nullable Bundle savedInstanceState) {
    NavController navController = Navigation.findNavController(view);
    AppBarConfiguration appBarConfiguration =
            new AppBarConfiguration.Builder(navController.getGraph()).build();
    Toolbar toolbar = view.findViewById(R.id.toolbar);

    NavigationUI.setupWithNavController(
            toolbar, navController, appBarConfiguration);
}

デスティネーションとメニュー項目を結び付ける

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 パネルです。 ドロワーは、ユーザーがアプリバー内のドロワー アイコン()をタップしたときや、画面の左端からスワイプしたときに表示されます。

ナビゲーション メニューを表示している開いたドロワー
図 3. ナビゲーション メニューを表示している開いたドロワー。

ドロワー アイコンは、DrawerLayout を使用しているすべてのトップレベル デスティネーション上で表示されます。

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

たとえば、下記のレイアウトの場合、2 つの子ビューを持つ DrawerLayout を使用しています。一方の子ビューは、メイン コンテンツを格納する NavHostFragment で、もう一方の子ビューは、ナビゲーション ドロワーのコンテンツ用の 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">

    <!-- Layout to contain contents of main body of screen (drawer will slide over this) -->
    <androidx.fragment.app.FragmentContainerView
        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 -->
    <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" />

</androidx.drawerlayout.widget.DrawerLayout>

次に、DrawerLayoutAppBarConfiguration に渡して、ナビゲーション グラフに接続します。次の例をご覧ください。

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 navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val navController = navHostFragment.navController
    findViewById<NavigationView>(R.id.nav_view)
        .setupWithNavController(navController)
}

Java

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

    ...

    NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
    NavController navController = navHostFragment.getNavController();
    NavigationView navView = findViewById(R.id.nav_view);
    NavigationUI.setupWithNavController(navView, navController);
}

ボトム ナビゲーション

NavigationUI は、ボトム ナビゲーションも処理できます。ユーザーがメニュー項目を選択すると、NavController は、onNavDestinationSelected() を呼び出して、ボトム ナビゲーション バー内で選択されているアイテムを自動的に更新します。

ボトム ナビゲーション バー
図 4. ボトム ナビゲーション バー。

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

<LinearLayout>
    ...
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        ... />
    <com.google.android.material.bottomnavigation.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 navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val navController = navHostFragment.navController
    findViewById<BottomNavigationView>(R.id.bottom_nav)
        .setupWithNavController(navController)
}

Java

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

    ...

    NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
    NavController navController = navHostFragment.getNavController();
    BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);
    NavigationUI.setupWithNavController(bottomNav, navController);
}

ボトム ナビゲーションを含む包括的な例については、GitHub の Android アーキテクチャ コンポーネントの高機能ナビゲーション サンプルをご覧ください。

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

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

NavController は、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);
       }
   }
});

参考情報

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

サンプル

Codelab

ブログ投稿

動画