Fresco: Shared element transition

Created on 28 Mar 2015  ·  57Comments  ·  Source: facebook/fresco

Shared element transition doesn't seem to work.

Tested in a motorola device with android 5.0

Most helpful comment

Here is the workaround I use that works fine:
https://github.com/bumptech/glide

All 57 comments

Can you give us more details? What exactly did you try to do, and what happened instead?

In the new android version, lollipop, we can use an ImageView as a shared element transition between activities. In that case the image make a smoth transition from one activity to the next one. When using the SimpleDraweeView it just disappear.

The code to test it should be something like this:

theme of the app:

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowContentTransitions">true</item>
        <item name="android:windowAllowEnterTransitionOverlap">true</item>
        <item name="android:windowAllowReturnTransitionOverlap">true</item>
    </style>

Activity1 Layout.xml:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/image"
        android:layout_width="@dimen/size_1"
        android:layout_height="@dimen/size_1"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"/>

</RelativeLayout>

Activity2 Layout.xml:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/image"
        android:layout_width="@dimen/size_2"
        android:layout_height="@dimen/size_2"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:transitionName="image_transition"/>

</RelativeLayout>

The code to start the second activity:

    Intent intent = new Intent(activity1, Activity2.class);
    ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity1, simpleDraweeView1, "image_transition");
    activity1.startActivity(intent, options.toBundle());

This code should make a transition between the activities where the image moves and resizes from the top left corner of the activity 1 to the bottom right corner of the activity 2. I have tested a similar code loading images from the network and it does not display the animation.

I think this is realted too https://github.com/facebook/fresco/issues/99

I suspect this has to do with attach/detach events that view gets when in transition. We'll have to investigate that.

This is something I can confirm as well. Shared Element transitions seem to be broken on Fresco. It would be great if we could get this fixed, as this is a very important feature moving forward.

Happened to me too. When setting transtitionName xml attr to SimpleDraweeView, setImageUri() method stopped working

Any updates on this ?

After the FadeDrawable finished animate real image, there is a log:
com.facebook.samples.comparison D/ViewRootImpl﹕ changeCanvasOpacity: opaque=false

maybe the changeCanvasOpacity cause the image not draw.

Is the bug fixed? It's very significant for my project as well. :)

package org.goodev.droidddle.drawee;

import com.facebook.drawee.generic.GenericDraweeHierarchy;
import com.facebook.drawee.view.SimpleDraweeView;

import android.content.Context;
import android.graphics.Matrix;
import android.util.AttributeSet;

public class TranslateDraweeView extends SimpleDraweeView {
public TranslateDraweeView(Context context) {
super(context);
}

public TranslateDraweeView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public TranslateDraweeView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

public TranslateDraweeView(Context context, GenericDraweeHierarchy hierarchy) {
    super(context, hierarchy);
}

// looks like overwrite this method can fix this issue
// but still don't figure out why
public void animateTransform(Matrix matrix) {
    invalidate();
}

}

@goodev there's not such a method to overwrite.

@shumin0809 just add this method, this is a hide public method for the transition.

I am seeing different, but still buggy behaviour with SharedElementTransitions. Don't know if this is the same, related, or separate issue.

_Reproduction_
Pretty standard setup for a SharedElementTransition:

  • RecyclerView in a fragment with GridLayoutManager displaying lots of images as SimpleDraweeViews, setting transitionName on them dynamically in the viewholder bind method (where I am also setting the uri)
  • OnClick fire a transition to a different fragment with a single SimpleDrawee view and the same transitionName also set dynamically

_Expected_
As for normal ImageViews (provided scaleType matches), seamless animation of shared resource between fragments

_Observed_
SharedElementTransition occurs but there is a flicker before the target image appears (seems that the target image isn't set early enough)

_Notes_
I have tried loading images from web and disk, and the image is loaded and in the cache before launching the transition. The target fragment's code is pulling the exact same uri. I fiddled around trying to use the pipeline directly (and learned a lot) but failed to affect the result.

_Setup_
Devices: Nexus 5 & Nexus 7 (2013), both running Android 5.1.1 (API 22)
Up to date library and tools: fresco (0.5.0), Android Studio (1.3 Preview 3 EAP.0), compileSdk (22), build tools (22.0.1)

_Realisations_
While writing this post I've noticed 2 things that might be relevant:

  • The two Drawees _are_ different sizes (this is not a problem for transitioning standard ImageViews and I don't remember anything about pre-fetching that sets the target dimensions)
  • From memory I think I am passing fresco the fragments as Context somewhere in the code, perhaps meaning the two fragments each have their own cache??

@jorgemf - Doesn't your posted code need a matching transitionName attribute set in the Activity 1 layout? https://developer.android.com/training/material/animations.html

_Start an activity with a shared element_
...
4 - Assign a common name to the shared elements in both layouts with the android:transitionName attribute.

For my issue it might just be that I need to postpone the transition. I'll have a look at this today:
http://www.androiddesignpatterns.com/2015/03/activity-postponed-shared-element-transitions-part3b.html

I'm with the same problem. I using fresco 0.5.3 and SimpleDraweeView not load image with attr "android:transitionName". Anybody know outher solution using Fresco?

@jorgemf If you set "android-background" in your Activity1 Layout.xml, it work not perfectly, but will work.
I set in my test it: android:background="@android:color/transparent"

@LuizGadao If you set a background you lose all the advantages of the library. I think it is pointless as you want it for images downloaded from internet. Not for static resources as a background.
After testing some libraries, picasso is working fine for me. No problems at all.

@jorgemf I agree with you. It is only a hack for do it work.

Any news on this one?

@LuizGadao can you post an example of your workaround XML?

Thank you for reporting this issue and appreciate your patience. We've notified the core team for an update on this issue. We're looking for a response within the next 30 days or the issue may be closed.

Any news on this one?

PS:
TranslateDraweeView provided by @goodev does not work correctly on some devices like XiaoMi2, HuaWei P8. ReenterTransition starts at wrong position.

Like as boxcounter said, Tra slateDraweeView doesn't work on HTC One M8, neither

If you are using ChangeImageTransform Transition, then I think the share element transition fails because ChangeImageTransform is animating the ImageView's Matrix which I think its not support by default DraweeView.

The problem I met is the shared element starts with "fitCenter" while originally it was "centerCrop".
The project can reproduce the issue
https://github.com/JackFan-Z/ActivitySharedElementTransition.git

Though I added a workaround
it doesn't work on my another private project using fresco.
Could someone find out where the real problem is?

Wrong start state of transition
2015-10-01 11 26 34

Before transition
2015-10-01 11 26 50

@JackFan-Z
Share element in transition is setting the final view to the initial values and animate it to the final values.

Normal ImageView combined with ChangeImageTransform will capture the start and end values of the image matrix and then animating the image matrix change.

However DraweeView overrides certain function related to image matrix so the ChangeImageTransform will not have any effect on the image.

So what you end up is only ChangeBounds taking effect.

Currently, I found no way to manipulate the image matrix of DraweeView. Even the setActualImageMatrix has been marked deprecated. Let assume it is not deprecated and it works exact like the default setImageMatrix of default ImageView. Even so It is really not very connivence to animate the change of it due to the fact that you don't have any way to change the matrix after you have set the DraweeViewHierarchy. So you end up need to create a new DraweeViewHierarchy on every onAnimationUpdate call.

I don't know why DraweeView implement in this way. And I am think the current possible solution is to get back the underlying Bitmap and create another ImageView to do the ChangeImageTransformTransition.

@soapsign
Thanks for your comment.

Actually in my private project, I did try create a dummy ImageView which take the same bitmap as shared element. The problem still exists.
I'm not sure whether the problem I'm facing is related to fresco or not.
But it's for sure that I can reproduce the same problem easily with SimpleDraweeView.

The ChangeImageTransform uses the intrinsic dimensions to determine the transformation matrix. Our implementation of DraweeView uses a DraweeHierarchy which that has intrinsic dimensions equals to -1 for width and height. This is because Drawee already applies correct scale type scaling and hence there is no need for a view to do so. Furthermore, ImageView can only apply one scale type whereas drawable hierarchy can use separate scale types for each image branch (placeholder, failure image, actual image, etc.). Returning actual intrinsic dimensions to the view just puts as on risk of having sizing bugs.
If you want transition working you should use ChangeBounds.

How about add a new option like "fresco:ImageMatrixSrc=actual" ?

@boxcounter At the moment we think what we have now is ok. But you could create a pull request for your proposal :)

@massimocarli ChangeBounds works well only when shared image has the same size on both activities/views. But when image should be resized during transition, it crops and looks bad.
What you propose to do in such way? ChangeImageTransform resolve this, but it isn't work in DraweeView.

Same here...

@massimocarli ChangeBounds isn't good enough.

This is what I'm trying to achieve (this is using ImageView): https://gfycat.com/HideousEarlyAndalusianhorse

This is what it looks like using SimpleDraweeView: https://gfycat.com/PracticalCorruptGrouper
Note the way the original image resizes incorrectly behind the animated image.

This is what it looks like with just ChangeBounds as the Transition: https://gfycat.com/SorrowfulExemplaryAntlion

Hey Folks, I have the same issue. ChangeBounds is not working smoothly. If there is a workaround or a fix, please tell us. Shared element transition is one of the best things that material design introduced and fresco is one of the finest library that I have used till now. Please let the developers use both seamlessly.

Has anyone been able to find a workaround since Fresco won't fix this?

Here is the workaround I use that works fine:
https://github.com/bumptech/glide

Hello everybody,

The ChangeBounds method itself isn't enough and I'm astonished by the answer from Facebook that the current state of shared elements transitions is ok.

I finally found a simple enough solution that seems to work for me on a sample project.
Since the ChangeBounds transition doesn't update the layout itself through onMeasure() but through onSizeChanged(), which is currently not overrided by any drawee view, the scale of the drawable is never updated during the transition.

Here is our CustomDraweeView which updates the TopLevelDrawable during the transition:

public class CustomDraweeView extends SimpleDraweeView {

    public CustomDraweeView(Context context, GenericDraweeHierarchy hierarchy) {
        super(context, hierarchy);
    }

    public CustomDraweeView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        Drawable drawable = getTopLevelDrawable();
        if (drawable != null) {
            drawable.setBounds(0, 0, w, h);
        }
    }
}

Here is the xml of the transitionset you will need to inflate:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:duration="@android:integer/config_mediumAnimTime"
    android:transitionOrdering="together"
    tools:targetApi="LOLLIPOP" >
    <changeBounds
        android:interpolator="@android:interpolator/accelerate_decelerate"/>
    <changeTransform
        android:interpolator="@android:interpolator/accelerate_decelerate"/>
</transitionSet>

The only downside I have with this method is when the scale type of the drawables don't match between the fragments or the activities, which you should avoid anyway if you want to use shared elements transitions.

@massimocarli @tyronen
Can anybody at Facebook provide some insight to why DraweeView doesn't currently override this method and to the possible problems that might crop up with this modification? If none is found, I would be happy to create a pull-request for that.

Hi all! In new Fresco release 0.10 with custom ScaleType transform between different ScaleTypes is trivial. Here is my implementation
https://gist.github.com/burzumrus/a589aa7e36ca003ddaf2334218c50ad0

Usage is simple

TransitionSet transitionSet = new TransitionSet();
transitionSet.addTransition(new ChangeBounds());
transitionSet.addTransition(new DraweeTransform(ScalingUtils.ScaleType.CENTER_CROP, ScalingUtils.ScaleType.FIT_CENTER));
getWindow().setSharedElementEnterTransition(transitionSet);

@burzumrus that's amazing! Thanks for implementing it. That's exactly what I had in mind with InterpolatingScaleType. Consider making a pull-request for Fresco if not done already.

@plamenko I am using fresco 0.12 and

getWindow().setSharedElementEnterTransition(DraweeTransition.createTransitionSet(
                    ScalingUtils.ScaleType.CENTER_CROP, ScalingUtils.ScaleType.CENTER_CROP));
getWindow().setSharedElementEnterTransition(DraweeTransition.createTransitionSet(
                    ScalingUtils.ScaleType.CENTER_CROP, ScalingUtils.ScaleType.CENTER_CROP));

The animation is working perfectly. But on return to first activity the image is disappearing

Did anyone try these with fragment-to-fragment animations? Because it doesn't seem to work on v0.12.
Edit: is it possible that the RecyclerView causes troubles?
Edit 2: Seems like the problem is that ChangeBounds itself only uses X and Y _window_ coords if the reparenting is set to true. Setting it via ChangeBounds's setReparenting(true) is deprecated and ChangeTransform is recommended instead. So for a RecyclerView, transitionSet.addTransition(new ChangeTransform()); is also required. (The returning animation seems bad still, but at least the entering animation is okay (except that the scale type has no effect changing startValues.view to endValues.view in createAnimator(...) solves this) with this.)

@Gericop and @ladia12 0.12 works well in my project in fragment-to-fragment animations.
I use it also in RecyclerView.
The thing is that you can only use 'replace' fragment transaction. You can't use 'add' transaction.
If it can help you, here is an example (without fresco) of fragment-to-fragment transition that helped me to start on something that worked (you can downloads the project code on github)
http://www.androidauthority.com/using-shared-element-transitions-activities-fragments-631996/

@sperochon I use replace and it's in a RecyclerView but it doesn't work (and honestly, it's surprising that it works for you, maybe you use a different version of RecyclerView? I use v24.1.1). I had to make a few changes in order to make the animations work:

  • added ChangeTransform to the transition set via transitionSet.addTransition(new ChangeTransform());

    • this is due to the fact that the ChangeBounds reports incorrect position of the start view in a RecyclerView (it always returns the first element's X,Y coords)

  • in createAnimator(...) replace if (mFromScale == mToScale) with if(mFromScale == mToScale && startBounds.equals(endBounds))

    • the transform won't happen otherwise if the two drawees share the same scale type, even though their sizes are not the same

  • in createAnimator(...) replace final GenericDraweeView draweeView = (GenericDraweeView) startValues.view; with final GenericDraweeView draweeView = (GenericDraweeView) endValues.view; (mind the startValues -> endValues change)

    • use the end drawee instead of the starting one

  • in AnimatorUpdateListener after the scaleType.setValue(fraction) call, insert the following lines:
Drawable drawable = draweeView.getTopLevelDrawable();

if (drawable != null) {
    drawable.setBounds(0, 0, draweeView.getWidth(), draweeView.getHeight());
}

This last piece of code is based on @Aohayou's solution (because I couldn't make CustomDraweeView work).
Tested this on Android 5.0.2 with support lib version v24.1.1.

NOTE that this solution won't work if you animate between images with the same sizes.

@Gericop Here is a demo that it works fine. I've just commit it onto github. I tried to clean the code at maximum. Be careful: I used only 1 image in my recycler view because the transition name has to be different on each item of the recycler view in order to Fresco to work. So, to simplify, I used only 1 image with 1 transition name.
https://github.com/sperochon/DemoFrescoFragment2Fragment

Hope this help!

@sperochon Try it with multiple images.
By the way, I tested your demo with unmodified source. This is the result:

device-2016-08-14-220810_1

This is completely wrong. The end view starts from a different position and the scale type has no effect whatsoever. I don't know how can you say for this that "it works fine" because it clearly doesn't.

@Gericop I've just updated the code. I forgot to specify a different layout for the end fragment. Try again, please.

@sperochon In API 23 emulator, it works fine. On my device (API 21) it doesn't. Also tested it in an API 21 emulator, it doesn't work there either.
So the point is: the current implementation does not work on API 21 (didn't test it on API 22), but works on API 23.

Edit: your demo only tests the default ChangeBounds and ChangeTransform transitions, not the DraweeTransition implementation Facebook supplied.

Unfortunately, you're right... I've tested it on my devices:
Android 5.0 + Fresco v0.11/v0.12 -> KO
Android 6.0 + Fresco v0.11/v0.12 -> OK
Didn't notice it before...

@Gericop @sperochon I have written a blog post on medium about this. Please see if it helps.

@ladia12 That post has nothing to do with the problem I was (we were) facing... Your blog entry is about _inter-Activity_ transition, while my issue is related to _inter-Fragment_. Furthermore, the real bug is in how API 21 handles the ChangeBounds and/or ChangeTransform transitions where the Facebook supplied DraweeTransition doesn't help either. My solution, on the other hand, overcomes this, if the source and target images have different dimensions (width and/or height).

Just for info:
Android 5.0 + Fresco v0.11/v0.12/0.13 -> KO
Android >= 5.1 + Fresco v0.11/0.12/0.13 -> OK

@ladia12 How did you resolve the issue of the original image disappearing when returning to the first activity?

@dbrant I have the same issue, Have you find some way to resolve?

See #1446

@ladia12 How did you resolve the issue of the original image disappearing when returning to the first activity?

@ladia12 How did you resolve the issue of the original image disappearing when returning to the first activity? My original image is in recyclerview's viewholder

I ended up using Picasso for the shared element transition. It wasn't fixed
in Fresco. So Picasso is lightweight and there isn'y any problem using it
alongwith Fresco.

On Wed, Jun 19, 2019 at 5:11 PM bembem1011 notifications@github.com wrote:

@ladia12 https://github.com/ladia12 How did you resolve the issue of
the original image disappearing when returning to the first activity? My
original image is in recyclerview's viewholder


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/facebook/fresco/issues/22?email_source=notifications&email_token=AAXQ5WYUP5KBJYNBOZX7CCDP3ILM5A5CNFSM4A6ZMH32YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODYBSZCI#issuecomment-503524489,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAXQ5W5EXO5SV4R2PXVTG7LP3ILM5ANCNFSM4A6ZMH3Q
.

Was this page helpful?
0 / 5 - 0 ratings