Компонент архитектуры навигации - фрагменты диалога

Можно ли использовать новый компонент архитектуры навигации с помощью диалогового окна "Диалог"? Нужно ли создавать пользовательский навигатор?

Я хотел бы использовать их с новыми функциями в моем графике навигации.

Ответ 1

Нет, с версии 1.0.0-alpha01 нет поддержки диалогов в составе вашего навигационного графика. Вы должны просто продолжать использовать show(), чтобы показать DialogFragment.

Ответ 2

Май 2019 Обновление:

DialogFragment теперь полностью поддерживается, начиная с Navigation 2.1.0-alpha03, вы можете прочитать больше здесь и здесь

Старый ответ для навигации <= 2.1.0-alpha02:

Я поступил так:

1) Обновите библиотеку Navigation по крайней мере до версии 2.1.0-alpha01 и скопируйте оба файла этой измененной сущности в ваш проект.

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)

или аналогичные методы.

Ответ 3

Да, это возможно. Вы можете получить доступ к представлению родительского фрагмента из фрагмента диалога, вызвав getParentFragment(). GetView(). И использовать вид для навигации.

Вот пример

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

Ответ 4

Я создал собственный навигатор для 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
        }
    }
}

Ответ 5

Да Фреймворк сделан таким образом, что вы можете создать класс, расширяющий абстрактный класс Navigator для представлений, которые не выходят из коробки, и добавить его в ваш NavController с помощью метода getNavigatorProvider().addNavigator(Navigator navigator)

Если вы используете NavHostFragment, вам также понадобится расширить его, чтобы добавить пользовательский Navigator или просто создать свой собственный MyFragment реализующий интерфейс NavHost. Это настолько гибко, что вы можете создавать свои собственные параметры XML с пользовательскими атрибутами, определенными в values, так же, как вы создаете собственные представления. Как то так (не проверено):

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

Ответ 6

Один из вариантов - просто использовать обычный фрагмент и сделать его похожим на диалог. Я обнаружил, что это не стоило хлопот, поэтому я использовал стандартный способ, используя show(). Если вы настаиваете, посмотрите здесь, как это сделать.

Ответ 7

Выпущена версия 2.1.0-alpha03, поэтому мы наконец можем использовать DialogFragments. К сожалению для меня, у меня есть некоторые проблемы с backstack при использовании отменяемых диалогов. Возможно, у меня неправильная реализация моих диалогов..

[ПОСЛЕДНЯЯ РЕДАКТИРОВАТЬ] Моя реализация была хорошей, проблема связана с неправильным подсчетом диалогов для DialogFragmentNavigator, как описано в трекере проблем. В качестве обходного пути вы можете посмотреть мои рекомендации

Ответ 8

Да, это возможно сейчас. В первоначальном выпуске это было невозможно, но теперь из "androidx.navigation: navigation-фрагмент: 2.1.0-alpha03" этой версии навигации вы можете использовать фрагмент диалога в компоненте навигации.

Проверьте это: - Поддержка фрагмента диалога Naviagtion