SharedElementTransition image transition transits through blank image

72 Views Asked by At

I have a SharedElementTransition on an image, which goes from a recyclerview in fragment A to a CollapsingToolbarLayout in fragment B. I tried everything I thought of in order to have a clean transition, however each time it fails.

class DetailsFragment : Fragment(R.layout.fragment_details) {
    private var _binding: FragmentDetailsBinding? = null
    private val binding get() = _binding!!
    private val args: DetailsFragmentArgs by navArgs()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        _binding = FragmentDetailsBinding.bind(view)

        val animation = TransitionInflater.from(requireContext()).inflateTransition(android.R.transition.move)
        sharedElementEnterTransition = animation

        Glide.with(this)
            .load(args.show.imageUrl)
            .error(R.drawable.default_image)
            .into(binding.detailsShowImage)
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

As it is, the image loads while the transition is executing, and there's a split moment where the image is white (there's none) and then the image is loaded. My goal here is to remove this 'white image'.

I tried to use a listener with Glide and postpone the transition with postponeEnterTransition() like so:

Glide.with(this)
    .load(args.show.imageUrl)
    .listener(object : RequestListener<Drawable> {
        override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
            startPostponeEnterTransition()
            return true
        }

        override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean                           
            startPostponeEnterTransition()
            return true
        }
    })
    .error(R.drawable.default_image)
    .into(binding.detailsShowImage)

However, with a very slow internet connexion, the image takes too long to load, which causes the transition to be delayed by sometimes several seconds. I tried putting a timeout with Glide's .timeout(TIMEOUT) but that still didn't work.

I may have done something wrong with my previous approach, and if so I'd be glad to hear the answer to my problem. But since I didn't find any, I switched to another approach, which is to modify the enter transition in order to modify the second image only when the transition ends (and if the image hasn't finished loading, to display a placeholder).

So I tried modifying the move transition provided by Android in order to keep the first image until the end of the transition, but I couldn't find how to achieve that. I saw that the move transition uses the below 4 transitions:

<transitionSet>
    <changeBounds/>
    <changeTransform/>
    <changeClipBounds/>
    <changeImageTransform/>
</transitionSet>

But my poor knowledge of transitions prevented me from knowing what to change in here, except from trying to remove them one by one hoping it does what I want...to which the answer is of course no. So here I am hoping for some insights on how to have a smooth and good-looking transition.

1

There are 1 best solutions below

0
Sambhav Khandelwal On

I can provide you with a solution but can't give you more information on animations/transitions.

So, in the Recylcer view also, it is pretty obvious that you are Glide to load the images. If you use the very same listener in the recycler view but instead store the drawable it provides and send it to the other activity using another class, it wont give you any issues. This is the code:

// this part of the code refers to the recycler view adapter's view holder

val drawableResource?: Drawable = null
Glide.with(this)
    .load(args.show.imageUrl)
    .listener(object : RequestListener<Drawable> {
        override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
            drawableResource = resource
            return true
        }

        override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean                           
            drawableResource = resource
            return true
        }
    })
    .error(R.drawable.default_image)
    .into(imageId)

parentView.setOnClickListener{
   val intent = Intent(holder.context, NextActivity::class.java)
   ObjectsHelper.resource = drawableResource
   startActivity(intent)
}

And now the ObjectsHelper class

public class ObjectsHelper(){
   companion object {
      var resource: Drawable? = null
   }
}

And finally in the second activity's onCreate() method:

image.setImageDrawable(ObjectsHelper.resource!!)

This shouldnt give you any errors.