之前已經(jīng)了解了navigation的基本用法,現(xiàn)在來看一下他的源碼,知其然更要知其所以然,這樣在遇到問題的時(shí)候才能知道怎么處理。以fragment中實(shí)現(xiàn)跳轉(zhuǎn)來看一下源碼中如何實(shí)現(xiàn)了跳轉(zhuǎn)的。
fragment中使用
findNavController().navigate(R.id.xxx)
點(diǎn)進(jìn)去看看實(shí)現(xiàn),在NavController中經(jīng)過一系列重載方法,最終調(diào)用了
@MainThread
public open fun navigate(
@IdRes resId: Int,
args: Bundle?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
) {
var finalNavOptions = navOptions
val currentNode = (
if (backQueue.isEmpty())
_graph
else
backQueue.last().destination
) ?: throw IllegalStateException("no current navigation node")
@IdRes
var destId = resId
val navAction = currentNode.getAction(resId)
var combinedArgs: Bundle? = null
...
val node = findDestination(destId)
...
navigate(node, combinedArgs, finalNavOptions, navigatorExtras)
}
這一步做的比較簡(jiǎn)單,就是解析參數(shù),并調(diào)用了
private fun navigate(
node: NavDestination,
args: Bundle?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
) {
...
navigator.navigateInternal(listOf(backStackEntry), navOptions, navigatorExtras) { navigated = true
addEntryToBackStack(node, finalArgs, it)
}
...
}
以上方法主要作了一些棧狀態(tài)保存等操作,跳轉(zhuǎn)核心代碼為
private fun Navigator<out NavDestination>.navigateInternal(
entries: List<NavBackStackEntry>,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?,
handler: (backStackEntry: NavBackStackEntry) -> Unit = {}
) {
navigate(entries, navOptions, navigatorExtras)
}
這里調(diào)用的是Navigator中的方法,在fragment里面的跳轉(zhuǎn)中使用的是FragmentNavigator的方法,
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) {
navigate(entry, navOptions, navigatorExtras)
}
}
最終調(diào)用為
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)
}
}
跳轉(zhuǎn)的核心代碼為ft.replace(containerId, frag),這里的FragmentManger為NavHostFragment創(chuàng)建的
@Deprecated("Use {@link #onCreateNavController(NavController)}")
protected open fun createFragmentNavigator(): Navigator<out FragmentNavigator.Destination> {
return FragmentNavigator(requireContext(), childFragmentManager, containerId)
}
也就是說,這里的FragmentManger為childFragmentManager,看到這里就明白了,NavHostFragment為占位用的,我們自己寫的Fragment由NavHostFragment控制的子fragment,內(nèi)部使用replace方式實(shí)現(xiàn)跳轉(zhuǎn)效果。