【问题标题】:Navigate without creating a new instance of Fragment - Navigation Component导航而不创建 Fragment 的新实例 - 导航组件
【发布时间】:2021-02-12 10:11:23
【问题描述】:

是否可以使用 Android 导航组件中的 navigate 函数而不创建片段的新实例但恢复之前的片段?

我尝试恢复之前的片段,但只有使用navigate函数才能在片段之间传输数据。

【问题讨论】:

  • 我不这么认为,如果你看一下函数 navigate 的源代码,你会发现它总是创建目标片段的新实例:f = clazz.newInstance();
  • @Alex 你是对的,这就是我寻找另一种解决方案的原因。我认为,最好的方法是使用视图模型。

标签: android android-architecture-navigation


【解决方案1】:

还没有,但我想他们正在努力。现在总是在创建新实例。但如果你自己编写导航器,这是可能的。

@Navigator.Name("keep_state_fragment") // `keep_state_fragment` is used in navigation xml
class KeepStateNavigator(
    private val context: Context,
    private val manager: FragmentManager, // Should pass childFragmentManager.
    private val containerId: Int
) : FragmentNavigator(context, manager, containerId) {

    override fun navigate(
        destination: Destination,
        args: Bundle?,
        navOptions: NavOptions?,
        navigatorExtras: Navigator.Extras?
    ): NavDestination? {
        val tag = destination.id.toString()
        val transaction = manager.beginTransaction()

        var initialNavigate = false
        val currentFragment = manager.primaryNavigationFragment
        if (currentFragment != null) {
            transaction.detach(currentFragment)
        } else {
            initialNavigate = true
        }

        var fragment = manager.findFragmentByTag(tag)
        if (fragment == null) {
            val className = destination.className
            fragment = manager.fragmentFactory.instantiate(context.classLoader, className)
            transaction.add(containerId, fragment, tag)
        } else {
            transaction.attach(fragment)
        }

        transaction.setPrimaryNavigationFragment(fragment)
        transaction.setReorderingAllowed(true)
        transaction.commitNow()

        return if (initialNavigate) {
            destination
        } else {
            null
        }
    }
}

那么你必须像下面这样调用

val navigator = KeepStateNavigator(this, navHostFragment.childFragmentManager, R.id.nav_host_fragment)
        navController.navigatorProvider.addProvider(navigator)

这是示例:Github

【讨论】:

    【解决方案2】:

    我相信您可以通过 ViewModelProvider 从活动中获取视图模型。

    inline fun <reified VM : ViewModel> BaseFragment.vmProviderActivity() = lazy {
    
        val viewModel = activity?.let { ViewModelProviders.of(it).get(VM::class.java) }
    
        if (viewModel is BaseViewModel) {
            viewModel.kodeinInstance = kodein
        }
    
        return@lazy viewModel
    }
    

    【讨论】:

      【解决方案3】:

      我改进了 Umut ADALI 的答案:

      /**
       * Created by Nooi on 12,February,2021
       */
      @Navigator.Name("fragment")
      class PersistentFragmentNavigator(
          persistentClasses : Set<KClass<out Fragment>>,
          context : Context,
          manager : FragmentManager,
          containerId : Int
      ) : FragmentNavigator(context, manager, containerId) {
      
          private val navigatorHandler = PersistentNavigatorHandler(persistentClasses, manager, containerId)
      
          init {
              navigatorHandler.addTopFragmentIfRequired()
          }
      
          override fun instantiateFragment(context : Context, fragmentManager : FragmentManager, className : String, args : Bundle?) : Fragment {
              return navigatorHandler.instantiateFragment(className) {
                  super.instantiateFragment(context, fragmentManager, className, args)
              }
          }
      }
      
      private class PersistentNavigatorHandler(
          private val persistentClasses : Set<KClass<out Fragment>>,
          private val manager : FragmentManager,
          private val containerId : Int
      ) {
      
          private val instances = mutableMapOf<String, Fragment>()
      
          fun addTopFragmentIfRequired() {
              manager.findFragmentById(containerId)
                  ?.takeIf { fragment ->
                      persistentClasses.any { it.qualifiedName == fragment.javaClass.name }
                  }
                  ?.let { fragment ->
                      instances[fragment.javaClass.name] = fragment
                  }
          }
      
          fun instantiateFragment(className : String, superCall : () -> Fragment) : Fragment {
              return instances[className] ?: superCall.invoke().also { fragment ->
                  if (persistentClasses.any { it.qualifiedName == className }) {
                      instances[className] = fragment
                  }
              }
          }
      }
      

      在获得navController和navHostFragment之后,将此代码添加到您的活动的onCreate中:

          navController.navigatorProvider.addNavigator(PersistentFragmentNavigator(
              persistentClasses = setOf(SomePersistentFragment::class),
              context = this,
              manager = navHostFragment.childFragmentManager,
              containerId = R.id.nav_host_fragment
          ))
      

      导航将与图形正常工作,但是当再次导航到标记为持久的片段时,会恢复之前的片段实例。在构建任何选项卡导航时非常有用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-26
        • 1970-01-01
        • 1970-01-01
        • 2012-12-02
        • 2021-08-29
        • 1970-01-01
        相关资源
        最近更新 更多