Material-components-android: [MaterialContainerTransform] Adding a transition listener may bug the return transition (screen transitioning away is the background, instead of previous screen)

Created on 9 Aug 2020  路  2Comments  路  Source: material-components/material-components-android

Description: Encountering an unusual issue. Have two fragments, Main, and Detail. Main will have a recycler view.

The MaterialContainerTransform is configured as follows:

  • Main: When tapping on a recycler view row, the row's root view has the shared transition name
  • Detail: The root view of this fragment has the shared transition name. A listener is added.

When tapping on a row in Main, the transition Main -> Detail works as expected; the tapped on row appears to expand into Detail.

Press back, and observe Detail -> Main return. Detail will shrink back into a recycler view row, however the background is still Detail, instead of Main.

Navigation is handled by the navigation component.

I noticed this issue some time ago. I believe the last version that seemed to work was com.google.android.material:material:1.2.0-alpha06.

Expected behavior: As Detail shrinks back into the recycler view row of the previous screen, Main, the background should be Main.

Source code: https://github.com/danejung/material_container_transform_issue

Android API version: API 29

Device: Pixel 2, emulator also works

Material Library version: com.google.android.material:material:1.3.0-alpha02

bug

Most helpful comment

Okay, so it looks like this is happening because we internally add a listener to MaterialContainerTransform to control when TransitionDrawable is added as an overlay. I believe we need to remove our listener internally after the transition ends to avoid a reused transform from running both our enter and return listeners.

For the moment, you can work around this in 1.3.0-alpha02 by setting a dedicated sharedElementReturntTransition.

In your DetialsFragment:

        val transformation = MaterialContainerTransform().apply {
            fadeMode = MaterialContainerTransform.FADE_MODE_CROSS
            duration = ExampleGodClass.TRANSITION_TIME_MS
            addListener(this@DetailFragment) // comment this out, issue should disappear
        }
        val returnTransformation = MaterialContainerTransform().apply {
            fadeMode = MaterialContainerTransform.FADE_MODE_CROSS
            duration = ExampleGodClass.TRANSITION_TIME_MS
        }
        sharedElementEnterTransition = transformation
        sharedElementReturnTransition = returnTransformation

All 2 comments

Thanks fo reporting this @danejung! I'll take a look

Okay, so it looks like this is happening because we internally add a listener to MaterialContainerTransform to control when TransitionDrawable is added as an overlay. I believe we need to remove our listener internally after the transition ends to avoid a reused transform from running both our enter and return listeners.

For the moment, you can work around this in 1.3.0-alpha02 by setting a dedicated sharedElementReturntTransition.

In your DetialsFragment:

        val transformation = MaterialContainerTransform().apply {
            fadeMode = MaterialContainerTransform.FADE_MODE_CROSS
            duration = ExampleGodClass.TRANSITION_TIME_MS
            addListener(this@DetailFragment) // comment this out, issue should disappear
        }
        val returnTransformation = MaterialContainerTransform().apply {
            fadeMode = MaterialContainerTransform.FADE_MODE_CROSS
            duration = ExampleGodClass.TRANSITION_TIME_MS
        }
        sharedElementEnterTransition = transformation
        sharedElementReturnTransition = returnTransformation
Was this page helpful?
0 / 5 - 0 ratings

Related issues

JavierSegoviaCordoba picture JavierSegoviaCordoba  路  3Comments

zkovar picture zkovar  路  3Comments

Mirmuhsin picture Mirmuhsin  路  3Comments

JakeWharton picture JakeWharton  路  3Comments

KelvinPac picture KelvinPac  路  3Comments