Android 10 (Q) transition shared element view in Fragment with RecyclerView is being stucked at fixed position
Asked Answered
O

1

6

I usually solve problems by myself but this time im really getting mad and can't find proper fix.

Scenario:

I have two fragments let's say A and B.

In Fragment A I'm populating RecyclerView (later only RV) from Rest API.

In Fragment B I have "detail view" with CollapsingToolbarLayout.

When I click on item in RV I'm opening Fragment B with transition and one shared element which is AppCompatImageView where I set local drawable. In Fragment B is image inside CollapsingToolbarLayout.

Shared element transition works in Fragment B - image is moved correctly. Transition also works when I click on back button and image is moving back on it's original position in RV.

But here comes a problem which i can't resolve. In both Fragments that particular image is being stuck on position and when I'm scrolled RV or CollapsingToolbarLayout View is not changed - in Fragment A image is not moving when scrolling RV and in Fragment B image is not hiding on collapse/expand changes.

Do anybody faced this issue because i don't and really don't understand that kind of behaviour. Never happened to me after years of development.

Here is screenshot of Fragment A after going back from Fragment B:

Here i am executing fragment transaction with transition:

fun replaceFragmentWithTransition(context: Context,
                                          sharedElement: View,
                                          fm: FragmentManager?,
                                          layoutContainer: Int,
                                          fragment: Fragment,
                                          tag: String,
                                          addToBackStack: Boolean = false) {
            fragment.sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(R.transition.default_transition)
            fragment.sharedElementReturnTransition = TransitionInflater.from(context).inflateTransition(R.transition.default_transition)
            val ft = fm?.beginTransaction()
            ft?.addSharedElement(sharedElement, sharedElement.transitionName)
            if (addToBackStack) { ft?.addToBackStack(null) }
            ft?.replace(layoutContainer, fragment, tag)
            ft?.commit()
        }

Here I'm setting return transition callback in Fragment A (called in onViewCreated):

setExitSharedElementCallback(object: SharedElementCallback() {
            override fun onMapSharedElements(names: MutableList<String>?, sharedElements: MutableMap<String, View>?) {
                Timber.d("onMapSharedElements")
                val vh = recyclerView.findViewHolderForAdapterPosition(selectedPanelIndex)
                if (vh != null && sharedElements != null && names != null) {
                    Timber.d("Size: ${sharedElements.size}")
                    sharedElements[names[0]] = vh.itemView.findViewById(R.id.imagePanel)
                }
            }
        })

Same in Fragment B but enter transition (called in onViewCreated):

setEnterSharedElementCallback(object: SharedElementCallback() {
            override fun onMapSharedElements(names: MutableList<String>?, sharedElements: MutableMap<String, View>?) {
                Timber.d("onMapSharedElements")
                if (names != null && sharedElements != null) {
                    Timber.d("Size: ${sharedElements.size}")
                    sharedElements[names[0]] = imagePanel
                }
            }
        })

In Fragment B also I assign transitionName into ImageView in onViewCreated function:

imagePanel.transitionName = transitionName

And also setting dynamic transition name in RV adapter:

inner class MyViewModel(override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer {

        fun bind(item: Panel, callback: (Panel, View) -> Unit) {
            imagePanel.transitionName = "${containerView.context.getString(R.string.text_transition_name_panel_img)}_$adapterPosition"
            item.getDrawableFromType().takeIf { it > 0 }?.let {
                imagePanel?.setImageDrawable(containerView.context.getDrawableCompat(it))
                imagePanel.show()
            } ?: imagePanel.hide()
            textName?.text = item.name
            containerView.onClick { callback(item, imagePanel) }
        }
    }

Device: Google Pixel 3, Android 10

Note that if i don't use transition callbacks return transition not working but issue with enter transition in Fragment B remains same.

I feel lost in this case. Any help will be appreciated. I tried many things. Thanks.


Update!:

Seems this issue is related to Android 10 only! I tried my old Xiaomi and it works. I created issue here so hope it will be solved. It's really annoying. I will keep updates in this one.

Overseas answered 28/9, 2019 at 10:22 Comment(8)
Have you tried on other devices below Android 10? I encountered the same issue you mentioned in Fragment B on Pixel 2 with Android 10, but the code works on API 28 (virtual device)! It's really annoying!!! Probably it's an issue related to 10 onlySwashbuckler
Once came to my mind that issue is related to Android 10 but i really like Android Q (permisions, performance, dark mode etc.) so i said goole cant mocking me like it with broken transitions... :( i will. try a other device with different OS and will let you know. Thanks for pointing out.Overseas
@li2 it works on my testing xiaomi which cost 100 euro. Im a little disappointed again with Google. I created issue: issuetracker.google.com/issues/141785904Overseas
Do you buy a new Phone specifically for this testing? What's the OS version of Xiaomi? I assume the issue is not relating to the brand. BTW, thanks for creating the issue.Swashbuckler
I think its related as you said into OS version. I have some old Xiaomi device for testing only. It has Android 8.1 (API 27) and all works smoothly. I later added more shared elements (currently three) and all working properly. @li2Overseas
did you use stable version of recycler view library?Openandshut
Having the exact same problem in the exact same scenario. Any luck?Austro
I found out that it happens with old 1.0.0 version of recyclerView library. In 1.1.0 version transition in this scenario works fine.Turbulence
S
5

Fixed by adding this lib

implementation "androidx.transition:transition:1.3.0-beta01"

Version 1.3.0-beta01

October 9, 2019

New features

  • Improved the integration with Fragment 1.2.0-beta01 to ensure that the Fragment’s View is not destroyed before the transition completes and that transitions are cancelled at the appropriate time. (aosp/1119841)

Version 1.2.0

October 9, 2019

Important changes since version 1.1.0

This version should be used if you're targeting API level 29. Otherwise, some of the transitions will not work properly. Instead of the reflection calls, this version uses the new public methods added in API Level 29. It is a part of our restrictions on non-SDK interfaces effort.

Source: https://developer.android.com/jetpack/androidx/releases/transition#1.3.0-beta01

Swashbuckler answered 12/10, 2019 at 12:50 Comment(2)
Please don't use "simply" and "lol". If it was so simple, people wouldn't come here for advice. I also didn't find the joke in adding a dependency. ¯_(ツ)_/¯Quinate
@EugenPechanec thanks for your advice. Actually I encountered the same issue, you can see the discussion below the question. The transition works on devices below Q without this dependency. I just express my curious why this can fix it by using that symbol. Now I understand by the release note you added here, thanksSwashbuckler

© 2022 - 2024 — McMap. All rights reserved.