Understanding exit/reenter shared element transitions

user2100277 picture user2100277 · Dec 7, 2014 · Viewed 16.1k times · Source

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;
    }
}

Answer

Alex Lockwood picture Alex Lockwood · Dec 7, 2014
  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.