可以使用 custom androidx.navigation.fragment.Navigator
。
我将演示如何覆盖fragment
导航。这是我们的自定义导航器。注意setAnimations()
方法
@Navigator.Name("fragment")
class MyAwesomeFragmentNavigator(
private val context: Context,
private val manager: FragmentManager, // Should pass childFragmentManager.
private val containerId: Int
): FragmentNavigator(context, manager, containerId) {
private val backStack by lazy {
this::class.java.superclass!!.getDeclaredField("mBackStack").let {
it.isAccessible = true
it.get(this) as ArrayDeque<Integer>
}
}
override fun navigate(destination: Destination, args: Bundle?, navOptions: NavOptions?, navigatorExtras: Navigator.Extras?): NavDestination? {
if (manager.isStateSaved) {
logi("Ignoring navigate() call: FragmentManager has already"
+ " saved its state")
return null
}
var className = destination.className
if (className[0] == '.') {
className = context.packageName + className
}
val frag = instantiateFragment(context, manager,
className, args)
frag.arguments = args
val ft = manager.beginTransaction()
navOptions?.let { setAnimations(it, ft) }
ft.replace(containerId, frag)
ft.setPrimaryNavigationFragment(frag)
@IdRes val destId = destination.id
val initialNavigation = backStack.isEmpty()
// TODO Build first class singleTop behavior for fragments
val isSingleTopReplacement = (navOptions != null && !initialNavigation
&& navOptions.shouldLaunchSingleTop()
&& backStack.peekLast()?.toInt() == destId)
val isAdded: Boolean
isAdded = if (initialNavigation) {
true
} else if (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
manager.popBackStack(
generateBackStackName(backStack.size, backStack.peekLast()!!.toInt()),
FragmentManager.POP_BACK_STACK_INCLUSIVE)
ft.addToBackStack(generateBackStackName(backStack.size, destId))
}
false
} else {
ft.addToBackStack(generateBackStackName(backStack.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) {
backStack.add(Integer(destId))
destination
} else {
null
}
}
private fun setAnimations(navOptions: NavOptions, transaction: FragmentTransaction) {
transaction.setCustomAnimations(
navOptions.enterAnim.takeIf { it != -1 } ?: android.R.anim.fade_in,
navOptions.exitAnim.takeIf { it != -1 } ?: android.R.anim.fade_out,
navOptions.popEnterAnim.takeIf { it != -1 } ?: android.R.anim.fade_in,
navOptions.popExitAnim.takeIf { it != -1 } ?: android.R.anim.fade_out
)
}
private fun generateBackStackName(backStackIndex: Int, destId: Int): String? {
return "$backStackIndex-$destId"
}
}
在下一步中,我们必须将导航器添加到NavController
. 这是一个如何设置它的示例:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragmentContainer)!!
with (findNavController(R.id.fragmentContainer)) {
navigatorProvider += MyAwesomeFragmentNavigator(this@BaseContainerActivity, navHostFragment.childFragmentManager, R.id.fragmentContainer)
setGraph(navGraphId)
}
}
在 xml 中没有什么特别的 :)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment
android:id="@+id/fragmentContainer"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true" />
</LinearLayout>
现在图中的每个片段都将具有 alpha 转换