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;
}
}
You shouldn't call finishAfterTransition()
in onBackPressed()
. The Activity
super class will already do this for you.
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).
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.
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.