【问题标题】:Navigation Architecture Component - Dialog Fragments导航架构组件 - 对话框片段
【发布时间】:2018-10-23 00:15:57
【问题描述】:

是否可以将新的导航架构组件与 DialogFragment 一起使用?我必须创建自定义导航器吗?

我希望将它们与导航图中的新功能一起使用。

【问题讨论】:

    标签: android android-architecture-components android-jetpack android-architecture-navigation


    【解决方案1】:

    2019 年 5 月更新

    现在从Navigation 2.1.0开始全面支持DialogFragment,您可以阅读更多herehere

    导航的旧答案

    我是这样进行的:

    1)Navigation 库至少更新到版本2.1.0-alpha01 并在您的项目中复制此modified gist 的两个文件。

    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)
    

    或类似的方法。

    【讨论】:

    • 我已经尝试过上一个版本,它正在工作,但动画没有为我运行。我必须在对话框类中设置它们。这是否也发生在某人身上?
    • 如何处理其正负点击监听器。
    • @Pinakin 您直接在自定义DialogFragment 中处理结果,例如使用onCreateDialog 并设置setPositiveButtonsetNegativeButton 侦听器
    • 我可以为 TimePickerFragment 实现吗?
    • @Pinakin 如果您使用导航转到对话框片段,请将其视为导航到任何其他片段。您如何从导航到常规片段中获得结果? developer.android.com/training/basics/fragments/… 如果对话框由启动它的片段“拥有”并且您想要点击侦听器,则最好使用Dialog 并跳过导航。
    【解决方案2】:

    不,从 1.0.0-alpha01 构建开始,不支持将对话框作为导航图的一部分。您应该继续使用show() 来显示DialogFragment

    【讨论】:

    • 导航功能请求可以直接发送到public issue tracker - 请务必说明您的用例以及为什么您会发现导航图中的对话框很有用。
    • 对话计划自春季以来是否有任何更新?
    • @AdamHurwitz - 随时为existing issue 加注星标以获取更新。
    • 接受的答案不再有效,因为DialogFragment 现在支持2.1.0-alpha03 版本
    • 您只需将 NavGraph 中的 DialogFragment 更改为 Razor 在下面的答案!
    【解决方案3】:

    是的。该框架的制作方式是,您可以创建一个扩展 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
        }
    }
    

    【讨论】:

      【解决方案4】:

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

      这是一个例子

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

      【讨论】:

        【解决方案5】:

        我为 DialogFragment 创建了自定义导航器。

        示例为here
        (这只是示例,所以可能有任何问题。)

        @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 中不再起作用。
        【解决方案6】:

        Version 2.1.0-alpha03 已发布,因此我们终于可以使用 DialogFragments。对我来说不幸的是,在使用可取消对话框时,我遇到了一些后台堆栈问题。可能我的对话框实现有误..

        [后期编辑] 我的实现很好,问题与 DialogFragmentNavigator 的错误对话框计数有关,如issue tracker 中所述 作为一种解决方法,您可以查看on my recommendation

        【讨论】:

        • 我也遇到了这个问题。只需一次测试就足以发现这个问题,但他们发布了一个新版本的库,因为他们懒得测试它,所以它甚至无法工作。让您想知道他们的质量流程是什么样的。
        【解决方案7】:

        更新时间:

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

        【讨论】:

        • 这是更新的正确解决方案!为我工作!
        【解决方案8】:

        一种选择是仅使用常规片段并使其看起来类似于对话框。我发现这不值得麻烦,所以我使用了使用 show() 的标准方式。如果您坚持使用See here 的方式。

        【讨论】:

          【解决方案9】:

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

          看看这个:- Naviagtion dialog fragment support

          【讨论】:

            【解决方案10】:

            是的。在导航组件的最新更新中是可能的。您可以查看此链接以获得清晰的概念。 raywenderlich.com

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-12-07
              • 1970-01-01
              • 1970-01-01
              • 2018-10-20
              • 1970-01-01
              相关资源
              最近更新 更多