web-dev-qa-db-ja.com

ナビゲーションコンポーネントで複数のNavHostFragmentsを使用できますか?

次の段落を理解するのが難しい場合は、私が作成したフローチャートをご覧ください。

私は現在、3つのトップレベルの宛先を持つメモアプリを作成しています。トップレベルの宛先の1つ(NotesList)には、ユーザーが作成したメモのリストが表示されます。 NotesListには、FilterMenuの宛先を含む下部のモーダルシートを表示するフィルターボタンがあります。 FilterMenuには、クリックするとシートの内容を検索先に置き換える検索ボタンと、クリックするとシートの内容をすべてのメモに関連付けられたタグのリストを含むフラグメントに置き換えるタグという名前のボタンがあります(TagList先)。

enter image description here

青のすべてがトップレベルの目的地です。紫色のすべてがモーダルシートに存在します。

FilterMenu、Search、およびTagListがモーダルシートに表示されます。つまり、NotesListにはこれらのフラグメントが含まれ、これらのフラグメントに置き換えられません。これらは、NotesListよりも小さい画面の領域に存在します。ナビゲーションを使用すると、フラグメントが互いに置き換わります。

2つのNavHostを使用できますか? 1つはトップレベルの目的地用で、もう1つはモーダルシートの内容用ですか?もしそうなら、私はそれをどのように実装しますか?そうでない場合、この場合に推奨されることは何ですか?

17
AnEnigmaticBug

2つのナビゲーショングラフを作成して、必要な動作を実現できます。 1つは最上位の宛先用で、もう1つはモーダルシート用です。それらは独立している必要があり、相互にリンクがない必要があります。 「ナビゲーションサーフェス」は別のものであるため、1つのナビゲーショングラフのみを使用することはできません。メインナビゲーションの場合はアクティビティであり、モーダルボトムシートの場合はボトムシートウィンドウです(BottomSheetDialogFragmentの場合は実際には別のウィンドウです)。

理論的には、これは非常に簡単に実現できます。

  • _main_nav.xml_はSettingsNoteList、およびTrashを保持します
  • _filter_nav.xml_は、FilterMenuSearch、およびTagListを保持します

トップレベルでのバックナビゲーションが必要ない場合は、フラグメントトランザクションを使用して、ナビゲーションコントローラーなしでトップレベルを実行することもできます。

したがって、基本的には_(BottomSheet)DialogFragment_が必要であり、メイン/その他のNavHostから独立したNavHostが必要です。これは、次のクラスで実現できます。

dialog_fragment_modal_bottom_sheet.xml

_<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
        xmlns:Android="http://schemas.Android.com/apk/res/Android"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:id="@+id/filterNavHost"/>
_

ModalBottomSheetDialogFragment .kt

_class ModalBottomSheetDialogFragment : BottomSheetDialogFragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
        inflater.inflate(R.layout.dialog_fragment_modal_bottom_sheet, container, false)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // We can't inflate the NavHostFragment from XML because it will crash the 2nd time the dialog is opened
        val navHost = NavHostFragment()
        childFragmentManager.beginTransaction().replace(R.id.filterNavHost, navHost).commitNow()
        navHost.navController.setGraph(R.navigation.filter_nav)
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return super.onCreateDialog(savedInstanceState).apply {
            // Normally the dialog would close on back press. We override this behaviour and check if we can navigate back
            // If we can't navigate back we return false triggering the default implementation closing the dialog
            setOnKeyListener { _, keyCode, event ->
                if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
                    view?.findNavController()?.popBackStack() == true
                } else {
                    false
                }
            }
        }
    }
}
_

ここでは2つのトリックを行います。

  1. NavHostフラグメントを手動で作成する必要があります。それを直接XMLに入れると、IDがすでに使用されているため、ダイアログを2回開いたときにクラッシュします。

  2. ダイアログの戻るナビゲーションを上書きする必要があります。ダイアログはアクティビティの上にある別のウィンドウであるため、ActivityonBackPressed()は呼び出されません。代わりに、OnKeyListenerを追加し、戻るボタンを離すと(_ACTION_UP_)、NavControllerでバックスタックをポップできる(戻る)かどうかを確認します。バックスタックをポップできる場合は、trueを返し、バックイベントを消費します。ダイアログは開いたままになり、NavControllerは1ステップ戻ります。すでに開始点にある場合は、falseを返すとダイアログが閉じます。

これで、ダイアログ内にネストされたグラフを作成でき、外側のグラフは気になりません。ネストされたグラフでダイアログを表示するには、次を使用します。

_val dialog = ModalBottomSheetDialogFragment()
dialog.show(childFragmentManager, "filter-menu")
_

ModalBottomSheetDialogFragmentを_<dialog>_の宛先として_main_nav_に追加することもできますが、これはテストしていません。この機能は現在まだアルファ版であり、ナビゲーション2.1.0-alpha03で導入されました。これはまだアルファ版であるため、APIが変更される可能性があり、私は上記のコードを個人的に使用してダイアログを表示します。これがアルファ/ベータから外れるとすぐに、_main_nav.xml_で宛先を使用することをお勧めします。ダイアログを表示する別の方法は、ユーザーの観点からは何の違いもありません。

ナビゲーション構造を使用してサンプルアプリケーションを作成します ここではGitHub 。 2つの独立したグラフを使用して、両方のレベルでナビゲーションを戻します。あなたはそれが機能しているのを見ることができます ここYoutubeで 。メインナビゲーションにはボトムバーを使用しましたが、代わりに引き出しに置き換えることができます。

7
crysxd

2つのnavHostFragmentsを作成する簡単な方法は、別のnavigation.xmlファイルを作成することです。

たとえば私のアプリには、2つのnavHostsFragmentsがあります。

ナビゲーションフロー用に最初のものを定義したので、ユーザーがアプリに入ると、navHostFragmentであるログインフラグメントに移動します。

ユーザーがログインすると、新しいnavHostFragmentを含むmainActivityに転送されます。

そうすれば、新しいnavHostFragmentを含む新しいアクティビティを開始するたびに。

お役に立てれば

0
Gal