Hiccups in activity transitions with shared elements

Björn Hurling picture Björn Hurling · Oct 26, 2014 · Viewed 10.3k times · Source

I want to use the new Activity Transitions of Android Lollipop. But currently I am seeing very weird hickups during the animations. I hacked together the smallest sample I could think of.

This is what I did in a very short version:

  1. Enabled window content transitions in my styles.xml
  2. Referenced a very simple slide.xml as exit transition in my style
  3. Provided a android:transitionName for a shared element in both layouts
  4. Called ActivityOptions.makeSceneTransitionAnimation() with that name and the view I want to share
  5. Passed the resulting bundle to startActivity()

This is the behavior I see (try 10x slower animation speed to see what I mean): Right before the slide animation slides down the views that are not shared, those views jump down a little. They actually move a little apart.

But: This only happens the second time I run that animation (and every time afterwards). The first pass looks just fine. And also this only happens if I want to share an element with the next activity. Everything works fine if I don't try to share an element.

Here is my code:

values/styles.xml

<resources>
<style name="AppTheme" parent="android:Theme.Material.Light">
    <!-- enable window content transitions -->
    <item name="android:windowContentTransitions">true</item>

    <!-- specify exit transition -->
    <item name="android:windowExitTransition">@transition/slide</item>
</style>
</resources>

transition/slide.xml

<slide />

Layout for the main activity

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <View
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#0000ff" />

        <View
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#00ff00" />

        <View
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#0000ff" />

        <View
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#00ff00" />

    </LinearLayout>

    <!-- the actual element I want to share -->
    <View
        android:id="@+id/view"
        android:layout_width="56dp"
        android:layout_height="56dp"
        android:background="#ff0000"
        android:elevation="8dp"
        android:transitionName="view" />

</RelativeLayout>

MainActivity.java

public class MainActivity extends Activity implements View.OnClickListener {

    View mView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mView = findViewById(R.id.view);
        mView.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, mView, "view");

        Intent intent = new Intent(this, SecondaryActivity.class);
        startActivity(intent, options.toBundle());
    }


}

Answer

George Mount picture George Mount · Oct 27, 2014

It is possible that you are hitting a bug in the transitions library that, for some reason, wasn't discovered until after L was locked down.

In the bug, you must have a reenter transition and a shared element reenter transition. The default reenter transition is the same as the exit transition for both.

If, during the return transition, the shared element is transferred back to the calling Activity while the views are still entering, you will see a hiccup when it interrupts the return call. With the slide and explode transitions, you will also see that the next time you exit, they will start from where the hiccup happened, so you get a nice double-wammy.

So you have a few options to work around this bug until MR1:

  • Explicitly set the android:windowSharedElementReenterTransition to @null. Most of the time you don't need a shared element reenter transition because the called Activity will place the shared element in the right location for you.
  • Don't use an exit transition (set android:windowReenterTransition to @null)
  • Make sure the duration of the calling Activity's android:windowSharedElementReturnTransition is large enough so that the calling Activity's windowReenterTransition finishes first. This can be tricky because it can depend on device state -- the calling Activity may have been removed from memory and need to be restarted.
  • Set android:windowAllowReturnTransitionOverlap to false.