Навигация без создания нового экземпляра фрагмента - компонент навигации
Можно ли использовать navigate
функция из Android Navigation Component без создания нового экземпляра фрагмента, но как восстановить предыдущий?
Я пытался восстановить предыдущий фрагмент, но только с использованием navigate
Данные функции могут передаваться между фрагментами.
3 ответа
Пока нет, но я думаю, они над этим работают. На данный момент всегда создается новый экземпляр. Но если написать собственный навигатор, это возможно.
@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
Я улучшил ответ Умута АДАЛИ:
/**
* 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
}
}
}
}
Добавьте этот код в свою активность onCreate сразу после получения navController и navHostFragment:
navController.navigatorProvider.addNavigator(PersistentFragmentNavigator(
persistentClasses = setOf(SomePersistentFragment::class),
context = this,
manager = navHostFragment.childFragmentManager,
containerId = R.id.nav_host_fragment
))
Навигация по графу будет работать нормально, но при повторном переходе к фрагменту, отмеченному как постоянный, восстанавливается предыдущий экземпляр фрагмента. Очень полезно при построении любой навигации по вкладкам.
Я считаю, что вы можете получить модели просмотра 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
}