参考
开发环境基于SDK 32,IDE是Android studio 2021.3.1
Navigation依赖的版本是2.4.0
implementation "androidx.navigation:navigation-fragment:2.4.0"
implementation "androidx.navigation:navigation-ui:2.4.0"
2.4.0版本跟2.3.x版本的不同之处在于需要重写的navigate方法不同,2.3.x的FragmentNavigator用的是java,2.4.0用的是kotlin。
1、创建一个FixFragmentNavigator继承FragmentNavigator,记得一定要加上注解@Navigator
@Navigator.Name("fixFragment")
class FixFragmentNavigator(
private val mContext: Context,
private val mFragmentManager: FragmentManager,
private val mContainerId: Int
) : FragmentNavigator(
mContext, mFragmentManager, mContainerId
)
2、重写navigate方法,把FragmentNavigator的代码copy过来
override fun navigate(
entries: List<NavBackStackEntry>,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
) {
if (mFragmentManager.isStateSaved) {
Log.i(
TAG, "Ignoring navigate() call: FragmentManager has already saved its state"
)
return
}
// super.navigate(entries, navOptions, navigatorExtras)
for (entry in entries) {
navigate(entry, navOptions, navigatorExtras)
}
}
3、创建navigate方法,把FragmentNavigator的方法navigate(entry: NavBackStackEntry, navOptions: NavOptions?, navigatorExtras: Navigator.Extras?)也拷贝过来,可以看到逻辑跟2.3.x是一样的,不同的只是mBackStack变成了savedIds
private fun navigate(
entry: NavBackStackEntry,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
) {
val backStack = state.backStack.value
val initialNavigation = backStack.isEmpty()
val restoreState = (
navOptions != null && !initialNavigation &&
navOptions.shouldRestoreState() &&
savedIds.remove(entry.id)
)
if (restoreState) {
// Restore back stack does all the work to restore the entry
fragmentManager.restoreBackStack(entry.id)
state.push(entry)
return
}
val destination = entry.destination as Destination
val args = entry.arguments
var className = destination.className
if (className[0] == '.') {
className = context.packageName + className
}
val frag = fragmentManager.fragmentFactory.instantiate(context.classLoader, className)
frag.arguments = args
val ft = fragmentManager.beginTransaction()
var enterAnim = navOptions?.enterAnim ?: -1
var exitAnim = navOptions?.exitAnim ?: -1
var popEnterAnim = navOptions?.popEnterAnim ?: -1
var popExitAnim = navOptions?.popExitAnim ?: -1
if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
enterAnim = if (enterAnim != -1) enterAnim else 0
exitAnim = if (exitAnim != -1) exitAnim else 0
popEnterAnim = if (popEnterAnim != -1) popEnterAnim else 0
popExitAnim = if (popExitAnim != -1) popExitAnim else 0
ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
}
ft.replace(containerId, frag)
ft.setPrimaryNavigationFragment(frag)
@IdRes val destId = destination.id
// TODO Build first class singleTop behavior for fragments
val isSingleTopReplacement = (
navOptions != null && !initialNavigation &&
navOptions.shouldLaunchSingleTop() &&
backStack.last().destination.id == destId
)
val isAdded = when {
initialNavigation -> {
true
}
isSingleTopReplacement -> {
// Single Top means we only want one instance on the back stack
if (backStack.size > 1) {
// If the Fragment to be replaced is on the FragmentManager's
// back stack, a simple replace() isn't enough so we
// remove it from the back stack and put our replacement
// on the back stack in its place
fragmentManager.popBackStack(
entry.id,
FragmentManager.POP_BACK_STACK_INCLUSIVE
)
ft.addToBackStack(entry.id)
}
false
}
else -> {
ft.addToBackStack(entry.id)
true
}
}
if (navigatorExtras is Extras) {
for ((key, value) in navigatorExtras.sharedElements) {
ft.addSharedElement(key, value)
}
}
ft.setReorderingAllowed(true)
ft.commit()
// The commit succeeded, update our view of the world
if (isAdded) {
state.push(entry)
}
}
4、反射获取savedIds,把replace改成show和hide。特别要注意的是NavHostFragment一定不要hide
private fun navigate(
entry: NavBackStackEntry,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
) {
val savedIds = try {
val targetClass: Class<*> = this.javaClass.superclass
val obj = targetClass.cast(this) as FragmentNavigator
val field = targetClass.getDeclaredField("savedIds")
//修改访问限制
field.isAccessible = true
field[obj] as MutableSet<String>
} catch (e: Exception) {
e.printStackTrace()
mutableSetOf()
}
//.........省略
// val frag = mFragmentManager.fragmentFactory.instantiate(context.classLoader, className)
//把类名作为tag,寻找已存在的Fragment
//(如果想只针对个别fragment进行保活复用,可以在tag上做些标记比如加个前缀)
var frag = mFragmentManager.findFragmentByTag(className)
if (null == frag) {
//不存在,则创建
frag = mFragmentManager.fragmentFactory.instantiate(mContext.classLoader, className)
}
//.........省略
// ft.replace(containerId, frag)
//replace换成show和hide
val fragments = mFragmentManager.fragments
for (fragment in fragments) {
//NavHostFragment不能hide
if(fragment.isAdded && fragment.isVisible && fragment !is NavHostFragment) {
ft.hide(fragment)
}
}
if (!frag.isAdded) {
ft.add(mContainerId, frag, className)
}
ft.show(frag)
//.........省略
}
然后其他的就跟Android-Jetpack笔记-Navigation之Fragment支持复用这篇文章的一样就可以了。
最后还是要感谢大佬们的分享的知识