Chaque grande application contient de nombreuses façons de naviguer entre les écrans. Une bonne bibliothèque de navigation devrait aider le développeur à les implémenter. C'est avec cette pensée que j'ai abordé l'étude de cas avec des graphiques de navigation imbriqués.
Ceci est le deuxième de trois articles sur la mise en œuvre de cas de navigation à l'aide du composant de navigation.
:
: 4 — A, B, C D. A B C, C D, D — , C->D.
, , .
? «» XML- , :
<!-- company_flow__nav_graph.xml -->
<navigation
android:id="@+id/company_flow__nav_graph"
app:startDestination="@id/CompanyFragment">
<fragment
android:id="@+id/CompanyFragment"
android:name="ui.company.CompanyFragment">
<action
android:id="@+id/action__CompanyFragment__to__CompanyDetailsFragment"
app:destination="@id/CompanyDetailsFragment" />
</fragment>
<fragment
android:id="@+id/CompanyDetailsFragment"
android:name="ui.company.CompanyDetailsFragment"/>
</navigation>
action-:
<navigation
android:id="@+id/menu__search"
app:startDestination="@id/SearchContainerFragment">
<fragment
android:id="@+id/SearchContainerFragment"
android:name="ui.tabs.search.SearchContainerFragment">
<action
android:id="@+id/action__SearchContainerFragment__to__CompanyFlow"
app:destination="@id/company_flow__nav_graph" />
</fragment>
<include app:graph="@navigation/company_flow__nav_graph" />
</navigation>
, . ?
, Navigation Component , . back stack- popBackUp
popBackUpInclusive
XML, popBackStack
NavController
-.
, : Splash , mBackStack
NavController
- Splash- NavBackStackEntry
.
, , back stack- SplashFragment. ? , NavGraph
, Activity, – SplashFragment, FragmentNavigator.Destination
.
– NavController
- popBackStack
? back stack- NavController
-, , .
.
class CompanyDetailsFragment : Fragment(R.layout.fragment_company_details) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
finish_flow_button.setOnClickListener {
findNavController().popBackStack(R.id.company_flow__nav_graph, true)
}
}
}
: . , action XML- :
<fragment
android:id="@+id/CompanyDetailsFragment"
android:name="ui.company.CompanyDetailsFragment"
android:label="@string/fragment_company_details__title"
tools:layout="@layout/fragment_company_details">
<action
android:id="@+id/action__finishCompanyFlow"
app:popUpTo="@id/company_flow__nav_graph"
app:popUpToInclusive="true" />
</fragment>
NavController
:
findNavController().navigate(R.id.action__finishCompanyFlow)
- : navigate .
, . : - ?
, . Navigation Component 2.3 Google key-value – SavedStateHandle
. NavController
- – previousBackStackEntry
currentBackStackEntry
. Google - , .
// Flow screen
findNavController().previousBackStackEntry
?.savedStateHandle
?.set("some_key", "value")
// Screen that waits result
val result = findNavController().currentBackStackEntry
?.savedStateHandle
?.remove<String>("some_key")
, ? previousBackStackEntry
SavedStateHandle
, . :
fragment_company_details__button.setOnClickListener {
// Here we are inside nested navigation flow
findNavController().popBackStack(R.id.company_flow__nav_graph, true)
// At this line, "findNavController().currentBackStackEntry" means
// screen that STARTED current nested flow.
// So we can send the result!
findNavController().currentBackStackEntry
?.savedStateHandle
?.set(COMPANY_FLOW_RESULT_FLAG, true)
}
: findNavController().popBackStack
, popBackStack
– , ! , SavedStateHandle
currentBackStackEntry
. entry , .
, , , currentBackStackEntry
SavedStateHandle
. , , :
// Read result from nested navigation flow
val companyFlowResult = findNavController().currentBackStackEntry
?.savedStateHandle
?.remove<Boolean>(CompanyDetailsFragment.COMPANY_FLOW_RESULT_FLAG)
text__company_flow_result.text = "${companyFlowResult}"
- , ,
NavController.popBackStack
, . - -
SavedStateHandle
.
, .
– A B. B A, include
. , , A B, B.
B – A:
– B:
.
, BottomNavigationView, . , …
? , « , »? , ?
, :
:
. Auth- , , , : Auth- .
auth flow- action :
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/menu__profile"
app:startDestination="@id/ProfileContainerFragment">
<fragment
android:id="@+id/ProfileContainerFragment"
android:name="ui.tabs.profile.ProfileContainerFragment">
<action
android:id="@+id/action__ProfileContainerFragment__to__AuthFlow"
app:destination="@id/auth__nav_graph" />
</fragment>
<include app:graph="@navigation/auth__nav_graph" />
</navigation>
auth- , :
Activity, - / BottomNavigationView. NavigationAdvancedSample , .
? - BottomNavigationView ( , , Host- ), Auth- .
, .
action MainFragment- :
<!— app_nav_graph.xml —>
<fragment
android:id="@+id/SplashFragment"
android:name="com.aaglobal.jnc_playground.ui.splash.SplashFragment"/>
<fragment
android:id="@+id/MainFragment"
android:name="com.aaglobal.jnc_playground.ui.main.MainFragment">
<action
android:id="@+id/action__MainFragment__to__AuthFlow"
app:destination="@id/auth__nav_graph" />
</fragment>
<include app:graph="@navigation/auth__nav_graph" />
, action , :
fragment_profile_container__button__open_auth_flow.setOnClickListener {
findNavController().navigate(R.id.action__MainFragment__to__AuthFlow)
}
… IllegalArgumentException
, NavController
Host- .
«» NavController
, , «» NavController, action «» ( ) . action , , .
Navigation Component NavController
-, , – Navigation.findNavController
:
fragment_profile_container__button__open_auth_flow.setOnClickListener {
Navigation.findNavController(
requireActivity(),
R.id.activity_root__fragment__nav_host
).navigate(R.id.action__MainFragment__to__AuthFlow)
}
Back
, . : «Back», , . IllegalArgumentException
– NavController
, , NavController
.
, :
java.lang.IllegalArgumentException: No view found for id 0x7f08009a (com.aaglobal.jnc_playground:id/fragment_main__nav_host_container) for fragment NavHostFragment{5150965} (e58fc3a2-b046-4c80-9def-9ca40957502d) id=0x7f08009a bottomNavigation#0}
, «Back». AndroidX OnBackPressedCallback
. NavController
, , :
class StartAuthFragment : Fragment(R.layout.fragment_start_auth) {
private var callback: OnBackPressedCallback? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
Navigation.findNavController(
requireActivity(),
R.id.activity_root__fragment__nav_host
).popBackStack()
}
}.also {
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, it)
}
}
}
callback- , , auth-: «» NavController
, , .
! «»: auth-, OnBackPressedCallback
=(
, , auth- – «» NavController
-:
class FinishAuthFragment : Fragment(R.layout.fragment_finish_auth) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
fragment_finish_auth__button.setOnClickListener {
Navigation.findNavController(
requireActivity(),
R.id.activity_root__fragment__nav_host
).popBackStack(R.id.auth__nav_graph, true)
findNavController().currentBackStackEntry
?.savedStateHandle
?.set(AUTH_FLOW_RESULT_KEY, true)
}
}
}
- , , «»
NavController
. - , .
, Splash-. , . , , , – . , (, ), .
, .
- «Back» , « » ( Splash), .
- , .
, , OnBackPressedCallback
, , :
StartAuthFragment:
<fragment
android:id="@+id/StartAuthFragment"
android:name="com.aaglobal.jnc_playground.ui.auth.StartAuthFragment"
android:label="Start auth"
tools:layout="@layout/fragment_start_auth">
<argument
android:name="isFromSplashScreen"
android:defaultValue="false"
app:argType="boolean"
app:nullable="false" />
<action
android:id="@+id/action__StartAuthFragment__to__FinishAuthFragment"
app:destination="@id/FinishAuthFragment" />
</fragment>
OnBackPressedCallback
:
class StartAuthFragment : Fragment(R.layout.fragment_start_auth) {
private val args: StartAuthFragmentArgs by navArgs()
private var callback: OnBackPressedCallback? = null
private fun getOnBackPressedCallback(): OnBackPressedCallback {
return object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (args.isFromSplashScreen) {
requireActivity().finish()
} else {
Navigation.findNavController(
requireActivity(),
R.id.activity_root__fragment__nav_host
).popBackStack()
}
}
}
}
}
Single Activity, requireActivity().finish()
, .
. «-».
- : Navigation Component runtime- , -
@id
destination- . - – , , , Splash.
, destination-, , . runtime- — .
– back stack-, , , . : , , «» ( main auth Splash-, ), , .
– , auth-, , , . SplashFragment
-.
auth-:
// FinishAuthFragment.kt
fragment_finish_auth__button.setOnClickListener {
// Save hasAuthData flag in prefs
GlobalDI.getAuthRepository().putHasAuthDataFlag(true)
// Navigate back from auth flow
Navigation.findNavController(
requireActivity(),
R.id.activity_root__fragment__nav_host
).popBackStack(R.id.auth__nav_graph, true)
// Send signal about finishing flow
findNavController().currentBackStackEntry
?.savedStateHandle
?.set(AUTH_FLOW_RESULT_KEY, true)
}
SplashFragment
-:
// SplashFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val authResult = findNavController().currentBackStackEntry
?.savedStateHandle
?.remove<Boolean>(FinishAuthFragment.AUTH_FLOW_RESULT_KEY) == true
if (authResult) {
navigateToMainScreen()
return
}
}
- – .
- , .