导航架构组件 - 对话框片段

72

是否可以在DialogFragment中使用新的导航架构组件?我需要创建自定义导航器吗?

我很想在我的导航图中使用它们的新功能。

10个回答

91

2019年5月更新:

Navigation 2.1.0开始,完全支持DialogFragment,您可以在此处此处阅读更多信息。

Navigation <= 2.1.0-alpha02的旧答案:

我按照以下方式进行:

1)至少将Navigation库更新到版本2.1.0-alpha01,并将此修改后的要点的两个文件复制到您的项目中。

2)然后,在导航主机片段中,将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" />

3) 创建你的 DialogFragment 子类并使用以下代码将它们添加到你的 nav_graph.xml 中:

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

4) 现在可以从片段或活动中启动它们,使用以下代码:

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

或类似方法。


1
我已经尝试了最新版本,它可以运行,但是动画对我来说并没有运行。我不得不在对话框类中设置它们。有其他人也遇到了这个问题吗? - J.S.R - Silicornio
23
如何处理其正负点击监听器。 - Pinakin Kansara
1
@Pinakin,你可以直接在自定义的DialogFragment中处理结果,例如使用onCreateDialog并设置setPositiveButtonsetNegativeButton监听器。 - MatPag
我能在TimePickerFragment中实现吗? - Rizki Oktavia Ningrum
3
如果你使用导航去到对话框片段,请将其视为导航到任何其他片段。如果要从导航到常规片段中获取结果,应该怎么做呢?请参考:https://developer.android.com/training/basics/fragments/pass-data-between 如果对话框是由启动它的片段“拥有”,并且你想要单击监听器,那么最好使用 Dialog 并跳过导航。 - Victor Rendina

18

截至1.0.0-alpha01版本,导航图中没有对话框的支持。您应该继续使用show()方法来显示一个DialogFragment


1
导航功能的特性请求可以直接提交到公共问题跟踪器 - 请确保说明您的用例以及为什么您会发现在导航图中使用对话框很有用。 - ianhanniballake
1
自从春天以来,对话框计划有更新吗? - AdamHurwitz
4
@AdamHurwitz - 请放心将现有问题标记为更新。 - ianhanniballake
37
已接受的答案不再有效,因为DialogFragment现在已经支持2.1.0-alpha03版本。 - musooff
你只需要按照Razor下面的答案,在NavGraph中更改你的DialogFragment! - Andy
对话框目标现在已经支持,并可在导航 UI 组件中使用。 - Hashir Labs

5

是的,这是可能的。您可以通过调用getParentFragment().getView()从对话框片段访问父片段的视图。并使用该视图进行导航。

以下是示例:

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

5
是的。该框架可以通过创建一个继承Navigator抽象类的类来为不默认提供的视图创建视图导航器,并使用方法getNavigatorProvider().addNavigator(Navigator navigator)将其添加到您的NavController中。
如果您正在使用 NavHostFragment,则还需要扩展它以添加自定义Navigator或者只需创建实现NavHost接口的MyFragment。它非常灵活,你可以像创建自定义视图一样在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()
        }
    }
}

Usage

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

3

我为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
        }
    }
}

虽然该库处于alpha阶段,但它的变化非常快。这个实现在alpha11中已经不再适用了。 - Allan Veloso

1

版本2.1.0-alpha03已发布,因此我们终于可以使用DialogFragments。不幸的是,当使用可取消对话框时,我遇到了一些后退栈问题。可能我的对话框实现有问题...

[稍后编辑] 我的实现是好的,问题与问题跟踪器中描述的DialogFragmentNavigator的错误对话框计数有关。作为解决方法,您可以查看我的建议


我也遇到了这个问题。只需要一个简单的测试就足以发现这个问题,但他们发布了一个新版本的库,却因为懒得测试而无法正常工作。这让你想知道他们的质量流程是怎样的。 - Julio E. Rodríguez Cabañas

0

是的,在最新的Navigation组件更新中可以实现。您可以查看此链接以获得清晰的概念。raywenderlich.com


0

更新于:

implementation "androidx.navigation:navigation-ui-ktx:2.2.0-rc04"

并在 my_nav_graph.xml 中使用

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

1
这是更新和正确的解决方案!对我有效! - Andy

-1

一种选择是只使用常规片段并使其看起来类似于对话框。我发现这不值得麻烦,所以我使用了使用show()的标准方式。如果您坚持要了解如何实现此操作,请单击此处


-1

是的,现在可以了。在最初的版本中不可能,但是现在从“androidx.navigation:navigation-fragment:2.1.0-alpha03”这个导航版本开始,您可以在导航组件中使用对话框片段。

看看这个链接:导航对话框片段支持


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接