Setting Up the Splash Screen with Android API
Implementing Splash Screen with DataStore and Exit Animation
From Android 12, the splash screen is enforced at every cold start of the application. If there is no customisation, the app will show the icon and dismiss the splash screen when the new frame is available.
However, we can do more about it. For example: customize the icon, length, exit animation and more. It can get dicey at some points, mainly with a smooth transition, but this is the best way to implement it, to my knowledge.
Setting up the splash screen
Dependencies
Firstly, add this dependency to the app’s build.gradle
.
implementation "androidx.core:core-splashscreen:1.0.0"
Showing splash screen
To show a basic splash screen implement the following code into the MainActivty
of your application before setContent in the onCreate method.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen()
setContent {
MainCompose()
}
}
This will show the splash screen on devices with Android 11 and lower too. So, if you have custom implementation of splash screen, you need to remove it. If it is not done, the app will show double splash screen.
Customisation of the splash screen
The splash screen uses a set of parameters defined by an XML file. One for the bright theme and the second one for the dark theme. To create it, go to the res -> values. Create new XML files as here:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.AppSplashScreen" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/white</item>
<item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher_round</item>
<item name="windowSplashScreenAnimationDuration">500</item>
<item name="postSplashScreenTheme">@style/Theme.AppTheme</item>
<item name="android:windowSplashScreenBrandingImage">@drawable/...</item>
</style>
</resources>
Both of the files share the same content, which can be customised.
windowSplashScreenBackground
— hex colour of the backgroundwindowSplashScreenAnimatedIcon
— path to drawable to showwindowSplashScreenAnimationDuration
— duration in mspostSplasScreenTheme
— theme to be used after the splash screenandroid:windowSplashScreenBrandingImage
— drawable to be shown at the bottom of the splash screen. Available for devices with Android 12 and higher.
After your customisation, there is one finishing step and that is the addition of the splash screen to your manifest.
In your manifest put the created XML as the theme for the app.
<application
android:name="AppName"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/Theme.AppSplashScreen"> <!-- Here -->
<activity
android:name=".MainActivity"
android:exported="true">
...
Here is the temporary result:
Unfortunately, two things are missing. Firstly, the splash screen just automatically transitions into the view without waiting. Secondly, the transition is unnatural to me at least. So let's have look at how to tackle them.
Waiting for some job to be done
The splash screen can be timed via the XML property. Moreover, the condition can be defined as when the splash screen should be moved away.
For example, you can wait to load in some cache data, SharedProperties or DataStore data. Mainly, if you code the type-safe DataStore, you need to wait for the loading of a file and parsing of the data.
Waiting for the process to finish, we can add to the splash screen screensetKeepOnScreenCondition
to make a callback for the splash screen.
If the condition is true, the splash screen is visible. If it turns to false, the splash screen is removed. The splash screen takes periodic check on this condition.
private val vm: IntroViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen().apply {
// adding loading condition
setKeepOnScreenCondition {
return@setKeepOnScreenCondition vm.isLoading.value
}
}
setContent {
MainCompose()
}
}
@HiltViewModel
class IntroViewModel @Inject constructor(private val dataStore: DataStore<AppSettings>): ViewModel() {
// StateFlow to represent loading state
private val _isLoading = MutableStateFlow(true)
val isLoading = _isLoading.asStateFlow()
init {
viewModelScope.launch {
dataStore.data.collect {
// do your work with loaded data
_isLoading.value = false // turn off the loading flag
}
}
}
// other methods ...
}
If the isLoading
state turns false, the splash screen is removed, and the main activity is shown.
Apart from waiting for some quick process, you shouldn’t do too much work during the splash screen. It will evoke the feeling that the app is stuck for the user. It is better to move from the splash screen to the next screen. Afterwards, you can show a progress bar or placeholders indicating loading is taking place.
Exit animation of the splash screen
The splash screen gives us one more callback for it. The setOnExitAnimationListener
is triggered when the splash screen is ready to go away. If you implement the method on your own, do not forget to call at the end remove()
, because the splash screen will get stuck.
If you use only the remove method, there will be no animation.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen().apply {
setKeepOnScreenCondition {
return@setKeepOnScreenCondition viewModel.isLoading.value
}
setOnExitAnimationListener { splashScreen ->
// to remove splashscreen with no animation
splashScreen.remove()
}
}
setContent {
MainCompose()
}
}
The splash screen behaves as any other view, so the techniques applied to your views previously will work on the splash screen too. Try not to do something fancy. The user will see it at the start of the app.
If the splash screen should pop down in decelerated manner, you can do it like this.
setOnExitAnimationListener { splashScreen ->
// access to the splash screen and moving it down
ObjectAnimator.ofFloat(
splashScreen.view,
View.TRANSLATION_Y,
// from top to down
0f, splashScreen.view.height.toFloat()
).apply {
// deceleration interpolaror, duration
interpolator = DecelerateInterpolator()
duration = 500L
// do not forget to remove the splash screen
doOnEnd { splashScreen.remove() }
start()
}
}
Wrapping it up
Keep the splash screen as simple as possible. There is no need to add something sophisticated. It is better to jump directly to action because your app's purpose is the goal, not the splash screen. It is more about integrity with the Android ecosystem and some UI sugar here and there.
Thanks for reading!