navigation返回时会重复创建fragment在2.3.5之前的版本,大部分的修改方式(新版本已失效):
override fun navigate(
destination: Destination,
args: Bundle?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
): NavDestination? {
if (mFragmentManager.isStateSaved) {
Log.i(
TAG, "Ignoring navigate() call: FragmentManager has already"
+ " saved its state"
)
return null
}
var className = destination.className
if (className[0] == '.') {
className = mContext.packageName + className
}
//final Fragment frag = instantiateFragment(mContext, mManager,
// className, args);
//frag.setArguments(args);
val ft = mFragmentManager.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)
}
val fragment = mFragmentManager.primaryNavigationFragment
if (fragment != null) {
ft.setMaxLifecycle(fragment, Lifecycle.State.STARTED)
ft.hide(fragment)
}
var frag: Fragment? = null
val tag = destination.id.toString()
frag = mFragmentManager.findFragmentByTag(tag)
if (frag != null) {
ft.setMaxLifecycle(frag, Lifecycle.State.RESUMED)
ft.show(frag)
} else {
frag = instantiateFragment(mContext, mFragmentManager, className, args)
frag.arguments = args
ft.add(mContainerId, frag, tag)
}
//ft.replace(mContainerId, frag);
ft.setPrimaryNavigationFragment(frag)
@IdRes val destId = destination.id
var mBackStack: ArrayDeque<Int>? = null
try {
val field = FragmentNavigator::class.java.getDeclaredField("mBackStack")
field.isAccessible = true
mBackStack = field[this] as ArrayDeque<Int>
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
val initialNavigation = mBackStack!!.isEmpty()
// TODO Build first class singleTop behavior for fragments
val isSingleTopReplacement = (navOptions != null && !initialNavigation
&& navOptions.shouldLaunchSingleTop()
&& mBackStack.peekLast() == destId)
val isAdded: Boolean
isAdded = if (initialNavigation) {
true
} else if (isSingleTopReplacement) {
// Single Top means we only want one instance on the back stack
if (mBackStack.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
mFragmentManager.popBackStack(
generateBackStackName(mBackStack.size, mBackStack.peekLast()),
FragmentManager.POP_BACK_STACK_INCLUSIVE
)
ft.addToBackStack(generateBackStackName(mBackStack.size, destId))
}
false
} else {
ft.addToBackStack(generateBackStackName(mBackStack.size + 1, destId))
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
return if (isAdded) {
mBackStack.add(destId)
destination
} else {
null
}
}
在navigation高版本(大于2.4.0)中FragmentNavigator类被重写,所以上述方法失效了。
提供新版本的修改方式供参考下(只测试了2.5.3版本):
@Navigator.Name("fragment")
class FragmentNavigatorHideShow(
private val context: Context,
private val fragmentManager: FragmentManager,
private val containerId: Int
) : FragmentNavigator(context, fragmentManager, containerId) {
override fun navigate(
entries: List<NavBackStackEntry>,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
) {
if (fragmentManager.isStateSaved) {
Log.i(
TAG, "Ignoring navigate() call: FragmentManager has already saved its state"
)
return
}
for (entry in entries) {
navigateOver(entry, navOptions, navigatorExtras)
}
}
private fun navigateOver(
entry: NavBackStackEntry,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
) {
//***********前后版本不同点*initialNavigation******
val initialNavigation = state.backStack.value.isEmpty()
val backStack = state.backStack.value
val destination = entry.destination as Destination
@IdRes val destId = destination.id
var mBackStack: LinkedHashSet<String>?= null
try {
//***********前后版本不同点*反射获取的变量不一样******
val field = FragmentNavigator::class.java.getDeclaredField("savedIds")
field.isAccessible = true
mBackStack = field[this] as LinkedHashSet<String>
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
val restoreState = (
navOptions != null && !initialNavigation &&
navOptions.shouldRestoreState() &&
mBackStack!!.remove(entry.id)//***********前后版本不同点*mBackStack去的是反射值******
)
if (restoreState) {
// Restore back stack does all the work to restore the entry
fragmentManager.restoreBackStack(entry.id)
state.push(entry)
return
}
val ft = createFragmentTransactionOver(entry, navOptions)
// 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 (!initialNavigation) {
// ft.addToBackStack(entry.id)
// }
if (navigatorExtras is Extras) {
for ((key, value) in navigatorExtras.sharedElements) {
ft.addSharedElement(key, value)
}
}
ft.commit()
// The commit succeeded, update our view of the world
if (isAdded)
state.push(entry)
}
private fun createFragmentTransactionOver(
entry: NavBackStackEntry,
navOptions: NavOptions?
): FragmentTransaction {
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)
}
val fragment = fragmentManager.primaryNavigationFragment
if (fragment != null) {
ft.setMaxLifecycle(fragment, Lifecycle.State.STARTED)
ft.hide(fragment)
}
var frag: Fragment? = null
val tag = destination.id.toString()
frag = fragmentManager.findFragmentByTag(tag)
if (frag != null) {
ft.setMaxLifecycle(frag, Lifecycle.State.RESUMED)
ft.show(frag)
} else {
frag = instantiateFragment(context, fragmentManager, className, args)
frag.arguments = args
ft.add(containerId, frag, tag)
}
// ft.replace(containerId, frag)
ft.setPrimaryNavigationFragment(frag)
ft.setReorderingAllowed(true)
return ft
}
}
这三个方法调用,可以整合到第一个方法的for循环中,这里比较下可以看出来修改点
此修改暂时满足返回时不会重复创建fragment。