Android shared element transition from splash screen to main activity
Asked Answered
L

3

8

My application displays a splash screen for 1 second before displaying the main activity. Both the splash screen and main activity share a common image that is required to animate from the center of the splash screen to the top of the main activity layout.

Since it wasn't obvious how to accomplish this animation if the splash screen was implemented as a <layer-list> background image in the main activity (see Splash Screens the Right Way or How do I make a splash screen?), I decided to implement the splash screen as a normal activity and use a shared element transition to animate the image between the two activities. Initially, I used the following onCreate() implementation in the splash activity:

public class SplashActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);

        ImageView imageView = (ImageView)findViewById(R.id.imageView);
        String transitionName = ViewCompat.getTransitionName(imageView);
        Intent intent = new Intent(this, LoginActivity.class);
        ActivityOptionsCompat options =
                ActivityOptionsCompat.makeSceneTransitionAnimation(
                        this, imageView, transitionName);

        imageView.postDelayed(() -> {
            ActivityCompat.startActivity(SplashActivity.this, intent, options.toBundle());
            finish();
        }, 1000);
    }
}

There are two problems with this approach:

  1. Calling finish() immediately after calling startActivity() causes the splash activity window to be hidden/destroyed before the animation starts which results in the home screen temporarily flashing into view during the animation.
  2. Pressing back from the main activity automatically triggers a shared element return transition that results in the image appearing suspended over the home screen for 500 ms after the main activity window has been closed. The return transition fails because the splash activity has already called finish() and therefore is no longer on the back stack.

To solve the first problem, I wrapped the finish() call in a postDelay() Runnable to ensure that it only gets invoked once the shared element transition has completed. A 1500 ms delay works in my application, but that value should be adjusted to depending on the timing required by other use cases.

...

imageView.postDelayed(() -> {
    ActivityCompat.startActivity(SplashActivity.this, intent, options.toBundle());
    imageView.postDelayed(this::finish, 1500);
}, 1000);

To solve the second problem, I overrode the main activity's onBackPressed() method to directly call finish() thereby avoiding the default implementation's call to finishAfterTransitions(). This prevents the Activity from attempting to perform the shared element return transition.

@Override
public void onBackPressed() {
    finish();
}

Any alternative approaches or suggestions to improve this solution would be appreciated.

Lona answered 27/4, 2017 at 20:12 Comment(1)
Don't forget to remove Runnable if user press back button before delayed time.Gutty
S
1

I faced similar problem also.

One approach can be to not to call finish() in Splash screen and in next screen call finishAffinity() on onBackPressed().

Schwenk answered 12/11, 2018 at 6:48 Comment(0)
C
0

Why don't you use fragments that sharing same activity instead? I don't see a reason to use 2 different activities for such a simple things.

Cordon answered 27/4, 2017 at 20:20 Comment(3)
Good point, using fragment would solve both issues for this case. But this posting is more about how to handle the case when you need to perform a shared element transition between activities, and the first activity needs to be finished after it starts the second activity.Lona
@Monte Creasor As long as you use something common in the activity you want to finish, that long the activity will stay alive. I think you should think a bit out of the box. Better use fragments, one fragment instead of your splash screen, on fragment for instead of your second activity, and a third fragment as animated transition between them. Hope this idea may help you.Cordon
I don't think that it is a good practice to use Fragment in SplashActivitySchooner
T
0

Instead of finish(); use ActivityCompat. finishAfterTransition(this);

Trilobite answered 27/5, 2017 at 0:44 Comment(1)
If you are suggesting changing finish() to finishAfterTransition for the main activity, that will cause the framework to attempt a return transition to the splash activity which no longer exists (was finished) and the shared element image will hang in view for a short period before the app closes. If you are suggesting changing the finish() in the splash Activity's postDelayed call, there's no need to do this since finishAfterTransition() only works if the activity has undergone a previous entry transition (check out the comment header for finishAfterTransition() in the Android source).Lona

© 2022 - 2024 — McMap. All rights reserved.