参考

Android-Jetpack笔记-Navigation之Fragment支持复用

开发环境基于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,

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

} 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支持复用这篇文章的一样就可以了。

最后还是要感谢大佬们的分享的知识

参考阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: