Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 203
Simon Dorociak Avatar asked Sep 28 '19 10:09

Simon Dorociak


1 Answers

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

like image 106
li2 Avatar answered Sep 22 '22 16:09

li2