Используя компонент навигации, как правильно перемещаться вверх при использовании NavHostFragments, вложенных в другие NavHostFragments

Обратите внимание, что я не говорю о вложенных навигационных графиках здесь. Я специально говорю об использовании NavHostFragments как подразделы других NavHostFragments и использование NavigationComponent для перехода назад и вперед между вспомогательным и родительским фрагментами. Что, как говорится...

Как правильно использовать компонент навигации с фрагментами NavHostFragments внутри других фрагментов NavHostFragments?

Например: допустим, у меня есть MainActivity со следующим макетом:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            style="@style/Toolbar"/>
    </android.support.design.widget.AppBarLayout>
    <fragment
        android:id="@+id/navHost"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />
</LinearLayout>

Основная деятельность имеет NavHostFragment с идентификатором navHost который использует навигационный график nav_graph, который выглядит следующим образом:

<?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/nav_graph"
    app:startDestination="@id/fragmentA">
    <fragment
        android:id="@+id/fragmentA"
        android:name="com.example.nestednavfragmentstest.FragmentA"
        android:label="FragmentA"
        tools:layout="@layout/fragment_a">
        <action android:id="@+id/action_fragmentA_to_fragmentB" app:destination="@id/fragmentB"/>
    </fragment>
    <fragment
        android:id="@+id/fragmentB"
        android:name="com.example.nestednavfragmentstest.FragmentB"
        android:label="FragmentB"
        tools:layout="@layout/fragment_b"/>
</navigation>

По существу, navHost может перемещаться из FragmentA в FragmentB (скажем, нажатие кнопки). Но подождите, это еще не все...

Что если бы я хотел Fragment B содержать свой собственный внутренний NavHostFragment ? XML может выглядеть примерно так:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Fragment B"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>
    <fragment
        android:id="@+id/embeddedNavHostFragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="50dp"
        app:defaultNavHost="true"
        app:layout_constraintTop_toBottomOf="@id/textView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:navGraph="@navigation/inner_nav_graph"/>
</android.support.constraint.ConstraintLayout>

Как вы видете, FragmentB имеет свой NavHostFragment называется embeddedNavHostFragment, который имеет navGraph, который выглядит следующим образом:

<?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/inner_nav_graph"
    app:startDestination="@id/innerFragmentB1">
    <fragment
        android:id="@+id/innerFragmentB1"
        android:name="com.example.nestednavfragmentstest.InnerFragmentB1"
        android:label="InnerFragmentB1"
        tools:layout="@layout/inner_fragment_b1">
        <action android:id="@+id/action_innerFragmentB1_to_innerFragmentB2" app:destination="@id/innerFragmentB2"/>
    </fragment>
    <fragment
        android:id="@+id/innerFragmentB2"
        android:name="com.example.nestednavfragmentstest.InnerFragmentB2"
        android:label="InnerFragmentB2"
        tools:layout="@layout/inner_fragment_b2"/>
</navigation>

Как видите, этот внутренний NavHostFragment используется для навигации между двумя внутренними фрагментами InnerFragmentB1 а также InnerFragmentB2, Теперь по основному вопросу...

Как правильно переопределить onSupportNavigateUp() в MainActivity ?

Первоначально я сделал что-то вроде этого:

override fun onSupportNavigateUp(): Boolean {
    return findNavController(R.id.navHost).navigateUp()
}

Но в некоторых случаях это может привести к сбою из-за несоответствия стека назад. Например:

  1. MainActivity начинается с фрагмента A в navHost,
  2. Навигация через action_fragmentA_to_fragmentB в B (который начинается с B1 в embeddedNavHost)
  3. Навигация через action_innerFragmentB1_to_innerFragmentB2 от B1 в B2 (происходит на embeddedNavHost)
  4. Нажмите на (embeddedNavHost возвращается из B2 в B1 [желаемый результат])
  5. Нажмите на (navHost возвращается из B в A [желаемый результат])
  6. Перейдите снова через action_fragmentA_to_fragmentB в B
  7. IllegalArgumentException: navigation destination id/action_fragmentA_to_fragmentB [не желаемый результат]

В итоге я закончил:

override fun onSupportNavigateUp(): Boolean {
    val hostedFragment = navHost?.childFragmentManager?.primaryNavigationFragment
    return when(hostedFragment){
        is FragmentB -> {
            if(hostedFragment.isInnerFragmentB2Showing()){
                findNavController(R.id.embeddedNavHostFragment).navigateUp()
            } else {
                findNavController(R.id.navHost).navigateUp()
            }
        }
        is FragmentA -> {
            findNavController(R.id.navHost).navigateUp()
        }
        else -> false
    }
}

Это сработало. Там не было аварии. Но мне просто любопытно , есть ли более правильный / автоматизированный способ навигации вверх по встроенным NavHostFragments, Этот современный способ больше похож на обходной путь, чем на правильное решение. Есть ли другие альтернативы тому, как заставить это работать?

Кроме того, мне любопытно мнение людей о NavHostFragments встраиваются друг в друга. Это правильное использование? Выяснить, как правильно обращаться с кнопкой "назад", было нелегким и простым решением, так как не было документации по встраиванию. NavHostFragments внутри друг друга и перемещаясь вперед и назад между родительским и вспомогательным фрагментами. Итак, мне интересно, если причина отсутствия документации в том, что, возможно, это неправильное (или неожиданное) использование NavigationComponent и поэтому не рекомендуется.

Если вы хотите посмотреть мой пример проекта, пожалуйста, перейдите сюда

0 ответов

Другие вопросы по тегам