Komponen Navigasi-Jutsu, vol. 2 - grafik navigasi bersarang



Setiap aplikasi besar berisi banyak cara untuk berpindah antar layar. Perpustakaan navigasi yang baik akan membantu pengembang menerapkannya. Dengan pemikiran inilah saya mendekati studi kasus dengan grafik navigasi bersarang.



Ini adalah artikel kedua dari tiga artikel tentang mengimplementasikan kasus navigasi menggunakan Komponen Navigasi.



BottomNavigationView, — , .



?





:





: 4 — A, B, C D. A B C, C D, D — , C->D.



?

, Navigation Component-, BottomNavigationView ( Search Responses – A B):





, (C D):





C Search ( A), D Search:





C Responses, C->D Responses:





, , .



? «»‎ 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-, , .



.



flow popBackStack
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- :



action-
<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 - , .



SavedStateHandle
// 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. , , :



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», , . IllegalArgumentExceptionNavController , , 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 , , :



back- auth-
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
    }
}




  • – .
  • , .


, Navigation Component, , , .




All Articles