How to create a custom fragment that extends NavHostFragment with it's own back stack?

ⅰ亾dé卋堺 提交于 2021-02-05 09:13:25

问题


I'm looking for a generic way to create my custom fragment with that has OnBackPressedCallback and viewModel that extends NavHostFragment using navigation graph i intend to put as child fragments into it's back stack.

Normally i create NavHostFragment for each tab or fragment with their FragmentContainerView, it's easy but repetitive to create for each host with

fragment_nav_host_home.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nested_nav_host_fragment_home"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"

            app:defaultNavHost="false"
            app:navGraph="@navigation/nav_graph_home"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

and writing databinding for their layouts, setting and using id nested_nav_host_fragment_home

class HomeNavHostFragment : BaseDataBindingFragment<FragmentNavhostHomeBinding>() {
    override fun getLayoutRes(): Int = R.layout.fragment_navhost_home

    private val appbarViewModel by activityViewModels<AppbarViewModel>()

    private var navController: NavController? = null

    private val nestedNavHostFragmentId = R.id.nested_nav_host_fragment_home


    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)


        val nestedNavHostFragment =
            childFragmentManager.findFragmentById(nestedNavHostFragmentId) as? NavHostFragment
        navController = nestedNavHostFragment?.navController


        // Listen on back press
        listenOnBackPressed()

    }

    private fun listenOnBackPressed() {
        requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback)
    }


    override fun onResume() {
        super.onResume()
        callback.isEnabled = true

        // Set this navController as ViewModel's navController
        appbarViewModel.currentNavController.value = navController

    }

    override fun onPause() {
        super.onPause()
        callback.isEnabled = false
    }

    val callback = object : OnBackPressedCallback(false) {

        override fun handleOnBackPressed() {

            // Check if it's the root of nested fragments in this navhost
            if (navController?.currentDestination?.id == navController?.graph?.startDestination) {

                /*
                    Disable this callback because calls OnBackPressedDispatcher
                     gets invoked  calls this callback  gets stuck in a loop
                 */
                isEnabled = false
                requireActivity().onBackPressed()
                isEnabled = true

            } else {
                navController?.navigateUp()
            }

        }
    }

}

Instead i tried to write more generic class for creating NavHostFragment directly instead of putting it inside another fragment

class BaseNavHostFragment private constructor() : NavHostFragment() {

    private val appbarViewModel by activityViewModels<AppbarViewModel>()

    companion object {
        fun create(
            @NavigationRes navGraphId: Int,
            startDestinationArgs: Bundle? = null
        ): BaseNavHostFragment {
            return NavHostFragment.create(navGraphId, startDestinationArgs) as BaseNavHostFragment
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        listenOnBackPressed()
    }


    private fun listenOnBackPressed() {
        requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback)
    }


    override fun onResume() {
        super.onResume()
        callback.isEnabled = true
        // Set this navController as ViewModel's navController
        appbarViewModel.currentNavController.value = navController

    }

    override fun onPause() {
        super.onPause()
        callback.isEnabled = false
    }


    /**
     * This callback should be created with Disabled because on rotation ViewPager creates
     * NavHost fragments that are not on screen, destroys them afterwards but it might take
     * up to 5 seconds.
     *
     * ### Note: During that interval touching back button sometimes call incorrect [OnBackPressedCallback.handleOnBackPressed] instead of this one if callback is **ENABLED**
     */
    private val callback = object : OnBackPressedCallback(false) {

        override fun handleOnBackPressed() {

            // Check if it's the root of nested fragments in this nav host
            if (navController?.currentDestination?.id == navController?.graph?.startDestination) {

                /*
                    Disable this callback because calls OnBackPressedDispatcher
                     gets invoked  calls this callback  gets stuck in a loop
                 */
                isEnabled = false
                requireActivity().onBackPressed()
                isEnabled = true

            } else {
                navController?.navigateUp()
            }
        }
    }
}

When i call create function it returns ClassCastException. Any way to create a NavHostFragment only passing R.navitation.x is simple way for creating to add or replace with fragment manager or ViewPager2, but couldn't find how to create a fragment this way.

来源:https://stackoverflow.com/questions/62627332/how-to-create-a-custom-fragment-that-extends-navhostfragment-with-its-own-back

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!