动态功能模块片段作为ViewPager2 FragmentStateAdapter

问题描述

我的目的是将动态要素模块中的片段显示为ViewPager2的根片段。

我设法用BottomNavigationViewhere来签出,但是当BottomNavigationView位于片段中时,NavigationExtension 导致内存泄漏,我可能会尝试重新为BottomNavigationView编写另一个扩展以防止内存泄漏,但这可能会花费很多时间,并且还希望对动态功能模块中的片段使用ViewPager2

我还可以在应用模块中使用一个空的代理片段,并让该片段导航到动态功能模块中的片段,但这将意味着为每个页面创建不必要的片段。

我作为动态功能模块片段并作为BottomNavigationView的根使用的是

创建了一个片段,该片段扩展了DynamicNavHostFragment以动态创建一个片段。 NavHostFragment.create(navigationId)的问题是它没有返回类型T extends NavHostFragment,还是我找不到方法

/**
 * [DynamicNavHostFragment] creator class which
 * uses [BaseDynamicNavHostFragment.createDynamicNavHostFragment] function with navigation graph
 * parameter
 */
class BaseDynamicNavHostFragment : DynamicNavHostFragment() {

    private val navControllerviewmodel by activityviewmodels<NavControllerviewmodel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }
//    override fun onCreate(savedInstanceState: Bundle?) {
//        super.onCreate(savedInstanceState)
//        val navResId = arguments?.get(KEY_GRAPH_ID) as Int
//        val startDestinationArgs = arguments?.get(KEY_START_DESTINATION_ARGS) as? Bundle
//
//        findNavController().setGraph(navResId,startDestinationArgs)
//    }

    override fun onResume() {
        super.onResume()
        // Set this navController as viewmodel's navController
        navControllerviewmodel.currentNavController.value = Event(navController)
    }

    override fun onDestroyView() {
        navControllerviewmodel.currentNavController.value = Event(null)
        super.onDestroyView()
    }

    companion object {

        private const val KEY_GRAPH_ID = "android-support-nav:fragment:graphId"
        private const val KEY_START_DESTINATION_ARGS =
            "android-support-nav:fragment:startDestinationArgs"

        /**
         * Create a new NavHostFragment instance with an inflated [NavGraph] resource.
         *
         * @param graphResId resource id of the navigation graph to inflate
         * @param startDestinationArgs arguments to send to the start destination of the graph
         * @return a new NavHostFragment instance
         */

        @JvmStatic
        fun createDynamicNavHostFragment(
            @NavigationRes graphResId: Int,startDestinationArgs: Bundle? = null
        ): BaseDynamicNavHostFragment {

            if (graphResId == 0) throw NavigationException("Navigation graph id cannot be 0")

            val bundle: Bundle = Bundle().apply {
                putInt(KEY_GRAPH_ID,graphResId)

                if (startDestinationArgs != null) {
                    putBundle(KEY_START_DESTINATION_ARGS,startDestinationArgs)
                }
            }

            return BaseDynamicNavHostFragment().apply {
                arguments = bundle
            }
        }
    }
}

模仿NavHostFragment.create()的键和值与NavHostFragment源代码中使用的相同。

并更改了NavigationExtensions.kt中的gainNavHostFragment()函数

private fun obtainNavHostFragment(
    fragmentManager: FragmentManager,fragmentTag: String,navGraphId: Int,containerId: Int
): NavHostFragment {
    // If the Nav Host fragment exists,return it
    val existingFragment = fragmentManager.findFragmentByTag(fragmentTag) as NavHostFragment?
    existingFragment?.let { return it }

    // Otherwise,create it and return it.
    val navHostFragment = BaseDynamicNavHostFragment.createDynamicNavHostFragment(navGraphId)
    fragmentManager.beginTransaction()
        .add(containerId,navHostFragment,fragmentTag)
        .commitNow()
    return navHostFragment
}

Changed NavHostFragment.create()BaseDynamicNavHostFragment.createDynamicNavHostFragment(navGraphId)

在应用程序模块导航文件夹中创建的代理Navigation_graphs使用带有<include-dynamic>的动态功能模块图

<?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"
    android:id="@+id/nav_graph_dashboard_start"
    app:startDestination="@id/nav_graph_dashboard">

    <!-- Dashboard  dynamic feature module -->
    <include-dynamic
        android:id="@+id/nav_graph_dashboard"
        android:name="com.smarttoolfactory.dashboard"
        app:graphResName="nav_graph_dashboard"
        app:moduleName="dashboard">

    </include-dynamic>
</navigation>

和动态功能模块中

<?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_dashboard"
    app:moduleName="dashboard"
    app:startDestination="@id/dashboardFragment">

    <fragment
        android:id="@+id/dashboardFragment"
        android:name="com.smarttoolfactory.dashboard.DashboardFragment"
        android:label="Dashboard Fragment"
        tools:layout="@layout/fragment_dashboard" />

</navigation>

BottomNavigationView一起使用

val navGraphIds = listof(
    R.navigation.nav_graph_home,R.navigation.nav_graph_dashboard_start,R.navigation.nav_graph_notification_start,R.navigation.nav_graph_account_start
)

// Setup the bottom navigation view with a list of navigation graphs
val controller = bottomNavigationView.setupWithNavController(
    navGraphIds = navGraphIds,fragmentManager = childFragmentManager,containerId = R.id.nav_host_container,intent = requireActivity().intent
)

这对于BottomNavigationView很好用,并且对于ViewPager2实现相同的事情

我在下面创建了FragmentAdapter

class DynamicFragmentStateAdapter(fragmentManager: FragmentManager,lifecycle: Lifecycle) :
    NavigableFragmentStateAdapter(fragmentManager,lifecycle) {

    override fun getItemCount(): Int = 3

    override fun createFragment(position: Int): Fragment {
        return when (position) {

            // Dashboard nav graph
            0 ->
                BaseDynamicNavHostFragment
                    .createDynamicNavHostFragment(R.navigation.nav_graph_dashboard_start)

            // Notification nav graph
            1 ->
                BaseNavHostFragment
                    .createNavHostFragment(R.navigation.nav_graph_notification_start)

            // Account nav graph
            else ->
                BaseNavHostFragment
                    .createNavHostFragment(R.navigation.nav_graph_account_start)
        }
    }
}

我还检查了fragmentManager.replace()是否可以使用片段实例化进行导航,没有问题,但是上面的FragmentStateAdapter提示错误

java.lang.IllegalArgumentException: Must use non-zero containerViewId

我一直在努力解决它,但还没有找到解决方案。

一个sample您可以尝试,它现在在开发分支中名为MainFragmentWithViewPager的片段中。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)