web-dev-qa-db-ja.com

ナビゲーションアーキテクチャコンポーネント-ダイアログフラグメント

DialogFragmentで新しいナビゲーションアーキテクチャコンポーネントを使用することはできますか?カスタムナビゲーターを作成する必要がありますか?

ナビゲーショングラフの新機能でそれらを使用したいと思います。

32
Leonardo Deleon

いいえ、1.0.0-alpha01ビルドの時点では、ナビゲーショングラフの一部としてダイアログはサポートされていません。 DialogFragmentを表示するには、引き続き show() を使用する必要があります。

18
ianhanniballake

2019年5月の更新

DialogFragmentはNavigation 2.1.0-alpha03から完全にサポートされるようになりました。詳細は here および here をご覧ください。

ナビゲーションの古い回答<= 2.1.0-alpha02:

私は次のように進みました:

1)Navigationライブラリを少なくともバージョン2.1.0-alpha01に更新し、この両方のファイルをコピーします 変更されたGist =プロジェクト内。

3)次に、ナビゲーションホストフラグメントで、nameパラメーターをカスタムNavHostFragmentに変更します

<fragment
    Android:id="@+id/nav_Host_fragment"
    Android:name="com.example.app.navigation.MyNavHostFragment"
    app:defaultNavHost="true"
    app:navGraph="@navigation/nav_graph"
    Android:layout_width="0dp"
    Android:layout_height="0dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/toolbar" />

4)DialogFragmentサブクラスを作成し、nav_graph.xmlに追加します:

<dialog
    Android:id="@+id/my_dialog"
    Android:name="com.example.ui.MyDialogFragment"
    tools:layout="@layout/my_dialog" />

5)次に、フラグメントまたはアクティビティからそれらを起動します

findNavController().navigate(R.id.my_dialog)

または同様の方法。

25
MatPag

はい、可能です。getParentFragment()。getView()を呼び出すことにより、ダイアログフラグメントから親フラグメントのビューにアクセスできます。そして、ナビゲーションにビューを使用します。

以下に例を示します

Navigation.findNavController(getParentFragment().getView()).navigate(R.id.nextfragment);
3
Niyas

DialogFragmentのカスタムナビゲーターを作成しました。

サンプルは こちら です。
(サンプルにすぎないため、問題がある可能性があります。)

@Navigator.Name("dialog_fragment")
class DialogNavigator(
    private val fragmentManager: FragmentManager
) : Navigator<DialogNavigator.Destination>() {

    companion object {
        private const val TAG = "dialog"
    }

    override fun navigate(destination: Destination, args: Bundle?, 
            navOptions: NavOptions?, navigatorExtras: Extras?) {
        val fragment = destination.createFragment(args)
       fragment.setTargetFragment(fragmentManager.primaryNavigationFragment, 
               SimpleDialogArgs.fromBundle(args).requestCode)
        fragment.show(fragmentManager, TAG)
        dispatchOnNavigatorNavigated(destination.id, BACK_STACK_UNCHANGED)
    }

    override fun createDestination(): Destination {
        return Destination(this)
    }

    override fun popBackStack(): Boolean {
        return true
    }

    class Destination(
            navigator: Navigator<out NavDestination>
    ) : NavDestination(navigator) {

        private var fragmentClass: Class<out DialogFragment>? = null

        override fun onInflate(context: Context, attrs: AttributeSet) {
            super.onInflate(context, attrs)
            val a = context.resources.obtainAttributes(attrs,
                    R.styleable.FragmentNavigator)
            a.getString(R.styleable.FragmentNavigator_Android_name)
                    ?.let { className ->
                fragmentClass = parseClassFromName(context, className, 
                        DialogFragment::class.Java)
            }
            a.recycle()
        }

        fun createFragment(args: Bundle?): DialogFragment {
            val fragment = fragmentClass?.newInstance()
                ?: throw IllegalStateException("fragment class not set")
            args?.let {
                fragment.arguments = it
            }
            return fragment
        }
    }
}
2
STAR_ZERO

バージョン2.1.0-alpha03 がリリースされたため、DialogFragmentsを最終的に使用できます。残念ながら、キャンセル可能なダイアログを使用すると、バックスタックにいくつかの問題があります。おそらく、ダイアログの実装に問題があります。

[LATER-EDIT]実装は良好でした。 issue tracker で説明されているように、問題はDialogFragmentNavigatorの間違ったダイアログカウントに関連しています。私の推奨事項を見てください

1
Liviu

はい。フレームワークは、すぐに使用できるビューのNavigator抽象クラスを拡張するクラスを作成し、メソッドgetNavigatorProvider().addNavigator(Navigator navigator)NavControllerに追加できるように作成されます。

NavHostFragmentを使用している場合は、それを拡張してカスタムナビゲーターを追加するか、独自のMyFragmentを実装してNavHostインターフェイスを作成する必要もあります。カスタムビューを作成するように、valuesで定義されたカスタム属性を使用して独自のxmlパラメーターを作成できるほど柔軟性があります。このようなもの(テストされていない):

@Navigator.Name("dialog-fragment")
class DialogFragmentNavigator(
        val context: Context,
        private val fragmentManager: FragmentManager
) : Navigator<DialogFragmentNavigator.Destination>() {

    override fun navigate(destination: Destination, args: Bundle?,
                          navOptions: NavOptions?, navigatorExtras: Extras?
    ): NavDestination {
        val fragment = Class.forName(destination.name).newInstance() as DialogFragment
        fragment.show(fragmentManager, destination.id.toString())
        return destination
    }

    override fun createDestination(): Destination = Destination(this)

    override fun popBackStack() = fragmentManager.popBackStackImmediate()

    class Destination(navigator: DialogFragmentNavigator) : NavDestination(navigator) {

        // The value of <dialog-fragment app:name="com.example.MyFragmentDialog"/>
        lateinit var name: String

        override fun onInflate(context: Context, attrs: AttributeSet) {
            super.onInflate(context, attrs)
            val a = context.resources.obtainAttributes(
                    attrs, R.styleable.FragmentNavigator
            )
            name = a.getString(R.styleable.FragmentNavigator_Android_name)
                    ?: throw RuntimeException("Error while inflating XML. " +
                            "`name` attribute is required")
            a.recycle()
        }
    }
}

使用法

my_navigation.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"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:id="@+id/navigation"
    app:startDestination="@id/navigation_home">

    <fragment
        Android:id="@+id/navigation_assistant"
        Android:name="com.example.ui.HomeFragment"
        tools:layout="@layout/home">
        <action
            Android:id="@+id/action_nav_to_dialog"
            app:destination="@id/navigation_dialog" />
    </fragment>

    <dialog-fragment
        Android:id="@+id/navigation_dialog"
        Android:name="com.example.ui.MyDialogFragment"
        tools:layout="@layout/my_dialog" />

</navigation>    

ナビゲートするフラグメント。

class HomeFragment : Fragment(), NavHost {

    private val navControllerInternal: NavController by lazy(LazyThreadSafetyMode.NONE){
        NavController(context!!)
    }

    override fun getNavController(): NavController = navControllerInternal

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Built-in navigator for `fragment` XML tag
        navControllerInternal.navigatorProvider.addNavigator(
            FragmentNavigator(context!!, childFragmentManager, this.id)
        )
        // Your custom navigator for `dialog-fragment` XML tag
        navControllerInternal.navigatorProvider.addNavigator(
            DialogFragmentNavigator(context!!, childFragmentManager)
        )
        navControllerInternal.setGraph(R.navigation.my_navigation)
    }

    override fun onCreateView(inflater: LayoutInflater, 
                              container: ViewGroup?, savedInstanceState: Bundle?): View? {
        super.onCreateView(inflater, container, savedInstanceState)
        val view = inflater.inflate(R.layout.home)
        view.id = this.id

        view.button.setOnClickListener{
            getNavController().navigate(R.id.action_nav_to_dialog)
        }

        return view
    }
}
1
Allan Veloso

1つのオプションは、通常のフラグメントを使用して、ダイアログに似た外観にすることです。手間をかける価値はないことがわかったので、show()を使用した標準的な方法を使用しました。もしあなたが こちらを参照 でそれを行う方法を求めているなら。

0
Jeffrey

はい、可能です。最初のリリースではできませんでしたが、現在は「androidx.navigation:navigation-fragment:2.1.0-alpha03」からこのナビゲーションバージョンを使用して、ナビゲーションコンポーネントでダイアログフラグメントを使用できます。

これをチェックしてください: Naviagtion dialog fragment support

0
Deepak Rajput