How do I define default animations for Navigation Actions?

前端 未结 3 768
情话喂你
情话喂你 2020-12-24 01:32

I\'m using Android Studio 3.2 Canary 14 and The Navigation Architecture Component. With this you can define transition animations pretty much as you would when using Intents

3条回答
  •  孤城傲影
    2020-12-24 02:16

    It is possible with custom androidx.navigation.fragment.Navigator.

    I will demonstrate how to override fragment navigation. Here is our custom navigator. Pay attention to setAnimations() method

    @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
        }
    }
    
    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"
    }
    }
    

    In the next step we have to add the navigator to NavController. Here is an example how to set it:

     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)
        }
    }
    

    And nothing special in xml :)

    
    
    
    
    

    Now each fragment from graph will have alpha transitions

提交回复
热议问题