Search code examples
c#xmlxamarinxamarin.formsxamarin.android

Duplicate splashscreen on Xamarin Android


I created a splash screen using this article from MS:

https://learn.microsoft.com/en-us/xamarin/android/user-interface/splash-screen

the problem is that, when the app starts:

  1. it starts with a red background
  2. then loads the SplashScreen image
  3. then loads the MainActivity

How do I get rid of the 1st step? I would like to directly show the splashscreen.

In the MainActivity, I set the MainLauncher = false and the Theme = "@style/MainTheme":

[Activity(Label = "S", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = false, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]

splash_screen.xml:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">
  <item>
    <color android:color="@color/splash_background"/>
  </item>
  <item>
    <bitmap
        android:src="@drawable/splash_logo"
        android:tileMode="disabled" 
        /><!--deleted android:gravity="center" to get a fullscreen image-->
  </item>
</layer-list>

style.xml:

<?xml version="1.0" encoding="utf-8" ?>
<resources>
  <!-- Base theme applied no matter what API -->
  <style name="MainTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
    <!--If you are using revision 22.1 please use just windowNoTitle. Without android:-->
    <item name="windowNoTitle">true</item>
    <!--We will be using the toolbar so no need to show ActionBar-->
    <item name="windowActionBar">false</item>
    <!-- Set theme colors from http://www.google.com/design/spec/style/color.html#color-color-palette -->
    <!-- colorPrimary is used for the default action bar background -->
    <item name="colorPrimary">#2196F3</item>
    <!-- colorPrimaryDark is used for the status bar -->
    <item name="colorPrimaryDark">#1976D2</item>
    <!-- colorAccent is used as the default value for colorControlActivated
         which is used to tint widgets -->
    <item name="colorAccent">#FF4081</item>
    <!-- You can also set colorControlNormal, colorControlActivated
         colorControlHighlight and colorSwitchThumbNormal. -->
    <item name="windowActionModeOverlay">true</item>

    <item name="android:datePickerDialogTheme">@style/AppCompatDialogStyle</item>
  </style>

  <style name="MainTheme.Splash" parent ="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowBackground">@drawable/splash_screen</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowActionBar">true</item>
  </style>
  <style name="MainTheme" parent="MainTheme.Base">
  </style>
  <style name="AppCompatDialogStyle" parent="Theme.AppCompat.Light.Dialog">
    <item name="colorAccent">#FF4081</item>
  </style>
</resources>

SplashActivity.cs:

    [Activity(Theme = "@style/MainTheme.Splash", MainLauncher = true, NoHistory = true, ScreenOrientation = ScreenOrientation.Portrait) ]
    public class SplashActivity : AppCompatActivity
    {
        static readonly string TAG = "X:" + typeof(SplashActivity).Name;

        public override void OnCreate(Bundle savedInstanceState, PersistableBundle persistentState)
        {
            
            base.OnCreate(savedInstanceState, persistentState);
            
        }

        // Launches the startup task
        protected override void OnResume()
        {
            base.OnResume();

            Task startupWork = new Task(() => { SimulateStartup(); });
            startupWork.Start();
            SimulateStartup();
        }

        // Simulates background work that happens behind the splash screen
       async void SimulateStartup()
        {
            //Log.Debug(TAG, "Performing some startup work that takes a bit of time.");
            await Task.Delay(5000); // Simulate a bit of startup work.

            //Log.Debug(TAG, "Startup work is finished - starting MainActivity.");
            StartActivity(new Intent(Application.Context, typeof(MainActivity)));
        }

        public override void OnBackPressed() { }
    }
}

Solution

  • The splash screen requirements were changed in Android 12. The recommended way has changed. Here is how you would do this in a modern Xamarin.Android app:

    1. Add a NuGet reference to Xamarin.AndroidX.Core.SplashScreen
    2. Install the splash screen using the new Splash Screen API in activity OnCreate.
    3. Create a keep on screen condition to determine when the splash screen should go away.
    4. Update your theme in [Activity] or AndroidManifest.xml.

    MainActivity.cs:

    protected override void OnCreate(Bundle bundle)
    {
        var splashScreen = AndroidX.Core.SplashScreen.SplashScreen.InstallSplashScreen(this);
        splashScreen.SetKeepOnScreenCondition(new TrueKeepOnScreenCondition());
        ....
        base.OnCreate(bundle);
    }
    

    TrueKeepOnScreenCondition.cs:

    public class TrueKeepOnScreenCondition : Java.Lang.Object, SplashScreen.IKeepOnScreenCondition
    {
        [Preserve(Conditional = true)]
        public TrueKeepOnScreenCondition(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer)
        {
        }
    
        public TrueKeepOnScreenCondition()
        {
        }
    
        [Preserve(Conditional = true)]
        public bool ShouldKeepOnScreen()
        {
            return true;
        }
    }
    

    I am always returning true because I'm using MvvmCross and there is a separate process that will finish the splash activity when MvvmCross has finished loading.

    AndroidManifest.xml:

    <application android:theme="@style/Theme.App.Starting">
        ...
    </application>
    

    Resources\values\themes.xml:

    <resources xmlns:tools="http://schemas.android.com/tools">
      <style name="Theme.App" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <item name="colorPrimary">@color/your_primary_color</item>
        ...
      </style>
      <style name="Theme.App.Starting" parent="Theme.SplashScreen.IconBackground">
        <item name="windowSplashScreenBackground">#165cab</item>
        <item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher_foreground</item>
        <item name="postSplashScreenTheme">@style/Theme.App</item>
      </style>
    
    </resources>