Understanding exit/reenter shared element transiti

2019-02-02 10:56发布

问题:

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

回答1:

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.



回答2:

  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.