Используя компонент навигации, как правильно перемещаться вверх при использовании 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()
}
Но в некоторых случаях это может привести к сбою из-за несоответствия стека назад. Например:
MainActivity
начинается с фрагментаA
вnavHost
,- Навигация через
action_fragmentA_to_fragmentB
вB
(который начинается сB1
вembeddedNavHost
) - Навигация через
action_innerFragmentB1_to_innerFragmentB2
отB1
вB2
(происходит наembeddedNavHost
) - Нажмите на (
embeddedNavHost
возвращается изB2
вB1
[желаемый результат]) - Нажмите на (
navHost
возвращается изB
вA
[желаемый результат]) - Перейдите снова через
action_fragmentA_to_fragmentB
вB
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 и поэтому не рекомендуется.
Если вы хотите посмотреть мой пример проекта, пожалуйста, перейдите сюда