Understanding exit/reenter shared element transitions
Asked Answered
S

2

15

I'm doing some rudimentary exploration of Shared Element Transitions in Android L. The simple example I've setup has an image view translating from the top of the screen to the bottom of the screen during activity transitions and I've extended the transition duration so I can see things working. I've hit two problems so far trying to understand how Shared Element Transitions works.

1)When using only Enter/Return transitions (Exit/Reenter set to null). The enter transition is fine, but when the back button is pressed the view animates for a time, stops, then reappear in the final position. Seems similar to this question but I've set all the Exist/Reenter transitions to null so not sure why it happens.

2)When using only Exit/Reenter transitions (Enter/Return set to null). Nothing is happening, the view transitions down the screen like its following a default enter transition (300ms duration), and when back is pressed the view pops back to its original position.

How do I use Exit/Reenter transitions?

Here is my code:

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView"
        android:src="@drawable/ic_launcher"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Animate!"
        android:id="@+id/button"
        android:layout_centerVertical="true"
        android:layout_alignParentStart="true" />

</RelativeLayout>

activity_second.xml

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/imageView2"
    android:src="@drawable/ic_launcher"
    android:layout_alignParentBottom="true"
    android:layout_centerHorizontal="true" />

MainActivity.java

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
        getWindow().setAllowEnterTransitionOverlap(false);
        getWindow().setAllowReturnTransitionOverlap(false);


        getWindow().setSharedElementExitTransition(exitTransition());
        getWindow().setSharedElementReenterTransition(reenterTransition());
        //getWindow().setSharedElementExitTransition(null);
        //getWindow().setSharedElementReenterTransition(null);


        setContentView(R.layout.activity_main);

        final View iView = findViewById(R.id.imageView);
        iView.setTransitionName("image");

        final Button button = (Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                ActivityOptions options = ActivityOptions
                        .makeSceneTransitionAnimation(MainActivity.this, iView, "image");
                startActivity(intent, options.toBundle());
            }
        });
    }

    private Transition exitTransition() {
        ChangeBounds bounds = new ChangeBounds();
        bounds.setInterpolator(new BounceInterpolator());
        bounds.setDuration(2000);

        return bounds;
    }

    private Transition reenterTransition() {
        ChangeBounds bounds = new ChangeBounds();
        bounds.setInterpolator(new OvershootInterpolator());
        bounds.setDuration(2000);

        return bounds;
    }
}

SecondActivity.java

public class SecondActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
        getWindow().setAllowEnterTransitionOverlap(false);
        getWindow().setAllowReturnTransitionOverlap(false);


        //getWindow().setSharedElementEnterTransition(enterTransition());
        //getWindow().setSharedElementReturnTransition(returnTransition());
        getWindow().setSharedElementEnterTransition(null);
        getWindow().setSharedElementReturnTransition(null);


        setContentView(R.layout.activity_second);

        final View iView = findViewById(R.id.imageView2);
        iView.setTransitionName("image");
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        finishAfterTransition();
    }

    private Transition enterTransition() {
        ChangeBounds bounds = new ChangeBounds();
        bounds.setDuration(2000);

        return bounds;
    }

    private Transition returnTransition() {
        ChangeBounds bounds = new ChangeBounds();
        bounds.setInterpolator(new DecelerateInterpolator());
        bounds.setDuration(2000);

        return bounds;
    }
}
Smokedry answered 7/12, 2014 at 18:12 Comment(0)
D
9

As I recall, there is a bug in L that causes the shared element return transition to be interrupted if it takes longer than the reenter transition duration. If you adjust your reenter transition duration (on the calling Activity), that should fix the interruption problem until the bug is fixed in MR1.

The exit and reenter transitions are for executing stuff before the shared element is allowed to transition. For example, if you want to lift your shared element before transferring it, that would be done in the shared element exit transition. The reenter would be used to do the opposite -- drop the view after it was transferred back. Most apps don't need it, but it is there for the rare one that does.

Dedicate answered 8/12, 2014 at 1:34 Comment(5)
I have a couple follow up questions. (1) By MR1 do you mean version 5.0.1 or something else later down the road? (2) Is there any reason why exit/reenter shared element transitions are available for Activity transitions but are not available for Fragment transitions?Snowslide
(1) MR1 isn't released yet. I'm not sure what version it will have. (2) Yes! Fragment Transitions are executed as a result of some sort of removal(hide/detach/remove/replace) and some sort of add (show/attach/add/replace). When you execute a FragmentTransaction, you can't very well operate on the Fragment to trigger the shared element exit transition AND remove it at the same time.Dedicate
FYI, I just added a blog post to describe these. halfthought.wordpress.com/2014/12/08/…Dedicate
Cool, thanks for the post! BTW, I'm in the process of writing my own blog posts as well, beginning with this one. :)Snowslide
@GeorgeMount I am using ActivityOptionsCompat still facing same issue any suggestionsQuickwitted
S
13
  1. You shouldn't call finishAfterTransition() in onBackPressed(). The Activity super class will already do this for you.

  2. You should call requestFeature() before super.onCreate(). Requesting Window.FEATURE_ACTIVITY_TRANSITIONS is not necessary if you are using the Theme.Material theme (or similar).

  3. Calling setAllowEnterTransitionOverlap(false) and setAllowReturnTransitionOverlap(false) is redundant here. These determine the activity's window content transitions overlap... they don't affect the activity's shared element content transitions at all.

  4. Setting exit and reenter shared element transitions is rarely necessary. You almost always want to use enter and return transitions instead. If you set only the exit and reenter shared element transitions and leave the enter and return shared element transitions null, the called activity will have no way of knowing how to animate the shared elements when the transition begins, and the animation will appear to be broken.

Snowslide answered 7/12, 2014 at 18:27 Comment(3)
So what would be an example of a scenario where I would want to use exit/reenter transitions? Edit: Also I followed your advice, but looks like animation back is still hiccuping.Smokedry
I haven't found a good reason to use exit/reenter transitions yet... in fact, they aren't even available for Fragment transitions. Maybe someone else can give a good answer about that...Snowslide
Hi @AlexLockwood, could you take a look on a question I posted a few weeks ago about bringing to front activities on the activity stack with transitions enabled? Thanks in advance #31296098Perianth
D
9

As I recall, there is a bug in L that causes the shared element return transition to be interrupted if it takes longer than the reenter transition duration. If you adjust your reenter transition duration (on the calling Activity), that should fix the interruption problem until the bug is fixed in MR1.

The exit and reenter transitions are for executing stuff before the shared element is allowed to transition. For example, if you want to lift your shared element before transferring it, that would be done in the shared element exit transition. The reenter would be used to do the opposite -- drop the view after it was transferred back. Most apps don't need it, but it is there for the rare one that does.

Dedicate answered 8/12, 2014 at 1:34 Comment(5)
I have a couple follow up questions. (1) By MR1 do you mean version 5.0.1 or something else later down the road? (2) Is there any reason why exit/reenter shared element transitions are available for Activity transitions but are not available for Fragment transitions?Snowslide
(1) MR1 isn't released yet. I'm not sure what version it will have. (2) Yes! Fragment Transitions are executed as a result of some sort of removal(hide/detach/remove/replace) and some sort of add (show/attach/add/replace). When you execute a FragmentTransaction, you can't very well operate on the Fragment to trigger the shared element exit transition AND remove it at the same time.Dedicate
FYI, I just added a blog post to describe these. halfthought.wordpress.com/2014/12/08/…Dedicate
Cool, thanks for the post! BTW, I'm in the process of writing my own blog posts as well, beginning with this one. :)Snowslide
@GeorgeMount I am using ActivityOptionsCompat still facing same issue any suggestionsQuickwitted

© 2022 - 2024 — McMap. All rights reserved.