How to ensure drawing order of transition animation of fragments in Android?

Viewed 168

I am using Fragment Transition animation. I am not using XML files for animations. I have subclassed Transition. Both animations are played fine but outgoing fragment's animation is drawn over incoming fragment's animation. I want to make outgoing fragment's animation to be drawn behind incoming fragment's animation. I am using androidx.fragment.app.FragmentContainerViewas a container for fragment.

Ref: https://developer.android.com/reference/androidx/fragment/app/FragmentContainerView

This document says

Fragments using exit animations are drawn before all others for FragmentContainerView. This ensures that exiting Fragments do not appear on top of the view.

I am calling .replace(...) in supportFragmentManager.beginTransaction().

1 Answers

There is a long running bug in the Android Framework which has the Exit Transition animating below the Entry Transition. This is due to Transitions which extend Visibility drawing their animated values on a ViewOverlay.

You can see the Android Ticket Here: https://issuetracker.google.com/issues/37036000

I got around this by using the enterTransition to capture the outgoing view as a bitmap and then manipulating it myself. Below you can see my implementation for a replacement for Hold() transition:

exitTransition = HoldBackground(resources).setDuration(duration)
enterTransition = Slide(Gravity.RIGHT).addTarget(R.id.container).setDuration(duration)
class HoldBackground(val resources : Resources) : Transition() {

    init {
        duration = resources.getInteger(android.R.integer.config_mediumAnimTime).toLong()
    }
    var inProgress = false

    @Synchronized
    override fun captureStartValues(transitionValues: TransitionValues) {
        if(inProgress) return
        transitionValues.view.also { exitingView ->
            exitingView.parent.also { fragmentContainer ->
                if(fragmentContainer is View && exitingView.width > 0 && exitingView.height > 0) {
                    inProgress = true
                    val previousBackground = fragmentContainer.background
                    val bitmap = Bitmap.createBitmap(
                        exitingView.width,
                        exitingView.height,
                        Bitmap.Config.ARGB_8888
                    )
                    exitingView.draw(Canvas(bitmap))
                    fragmentContainer.background = BitmapDrawable(resources, bitmap)
                    // Simple animator so timing matches developer animation scales
                    val animator = ValueAnimator.ofInt(0, 1).setDuration(duration)
                    animator.addListener(object : BlankAnimatorListener() {
                        override fun onAnimationEnd(animation: Animator?) {
                            fragmentContainer.background = previousBackground
                            inProgress = false
                        }
                    })
                    animator.start()
                }
            }
        }
    }

    override fun captureEndValues(transitionValues: TransitionValues) {}
}

I would love to find another way as this is not efficient at all. If you find something let me know.

Related