8
votes

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.

3
Don't forget to remove Runnable if user press back button before delayed time.ARiF

3 Answers

0
votes

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.

0
votes

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

0
votes

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().