Splash Screen Animations - Basic Tutorial

Here's a quick guide to getting an animated loading screen into you app using default Android icons from scratch. It's not really that hard, but it can be a bit non-intuitive.

Prepare the Project

1) Create an empty compose project named SplashScreenTutorial if you want to keep things simple. This will save you having to think about theme names etc.

2) Add the required dependency to your app


dependencies {

// ...
// Splash screen dependency
implementation("androidx.core:core-splashscreen:1.0.1")
}

Create the Icon and Animation

1) Create a new icon for the animation
  • In your res/drawable folder, add a new Vector Asset.
  • I'm using trophy for this example so you can celebrate your success!
  • Be sure to set a good color that will show up nicely. I'm using 87DE5D
  • I've put the full res/drawable/outline_trophy_24.xml code below to make things easier
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp" android:tint="#87DE5D"
android:viewportHeight="960"
android:viewportWidth="960"
android:width="24dp">

<path android:fillColor="@android:color/white"
android:pathData="M280,840L280,760L440,760L440,636Q391,
625 352.5,594.5Q314,564 296,518Q221,509 170.5,452.5Q120,
396 120,320L120,280Q120,247 143.5,223.5Q167,200 200,
200L280,200L280,120L680,120L680,200L760,200Q793,
200 816.5,223.5Q840,247 840,280L840,320Q840,
396 789.5,452.5Q739,509 664,518Q646,564 607.5,
594.5Q569,625 520,636L520,760L680,760L680,
840L280,840ZM280,432L280,280L200,280L200,320Q200,
358 222,388.5Q244,419 280,432ZM480,560Q530,560 565,
525Q600,490 600,440L600,200L360,200L360,440Q360,490 395,
525Q430,560 480,560ZM680,432Q716,419 738,388.5Q760,
358 760,320L760,280L680,280L680,432ZM480,380Q480,380 480,
380Q480,380 480,380L480,380L480,380L480,380Q480,
380 480,380Q480,380 480,380Z"/>

</vector>

2) Next, we need to add a group label and rotation point. 
  • Notice the added <group and /group>.
  • Also, notice the pivotXandY. This relates to the viewportHeight/Width. Our animation will pivot at the center, so 480 for X and Y.
  • The width/height dp can be ignored.
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp" android:tint="#87DE5D"
android:viewportHeight="960"
android:viewportWidth="960"
android:width="24dp">
<group
android:name="animationGroup"
android:pivotX="480"
android:pivotY="480">

<path android:fillColor="@android:color/white"
android:pathData="M280,840L280,760L440,760L440,636Q391,
625 352.5,594.5Q314,564 296,518Q221,509 170.5,452.5Q120,
396 120,320L120,280Q120,247 143.5,223.5Q167,200 200,
200L280,200L280,120L680,120L680,200L760,200Q793,
200 816.5,223.5Q840,247 840,280L840,320Q840,
396 789.5,452.5Q739,509 664,518Q646,564 607.5,
594.5Q569,625 520,636L520,760L680,760L680,
840L280,840ZM280,432L280,280L200,280L200,320Q200,
358 222,388.5Q244,419 280,432ZM480,560Q530,560 565,
525Q600,490 600,440L600,200L360,200L360,440Q360,490 395,
525Q430,560 480,560ZM680,432Q716,419 738,388.5Q760,
358 760,320L760,280L680,280L680,432ZM480,380Q480,380 480,
380Q480,380 480,380L480,380L480,380L480,380Q480,
380 480,380Q480,380 480,380Z"/>
</group>
</vector>

3) Next, we create the animation sequence
  • Create a new folder in res called animator (res/animator)
  • Create a new xml - splash_animator.xml in that folder
  • We'll simply do a single 360 degree animation
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"
android:duration="1000"
android:valueType="floatType"/>

4) Now we need to create an animated vector
  • Duplicate outline_trophy_24.xml and name it animated_outline_trophy_24.xml
  • Delete the contents and replace with the following
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/outline_trophy_24">

<target
android:animation="@animator/splash_animator"
android:name="animationGroup" />

</animated-vector>

5) At this point, we should be able to preview our animation.
  • Build the project
  • Close the animated_outline_trophy_24.xml and reopen it
    • The IDE can be glitchy with animations sometimes and they won't play
  • Click the rewind and play buttons.
    • In the animation below, I go through the animation twice as it's sometimes not obvious why it isn't playing. Notice the clicks highlighted at the bottom.
    • If the animation won't play even after rebuilding and closing, be sure you have followed the steps. If so, you may need to try closing Android Studio and reopening it again and things like that.

Great. We're mostly done at this point. We just to make our theme.

Create Splash Theme and Connect 

1) Open your res/values/themes/themes.xml to verify this appears as expected:
<?xml version="1.0" encoding="utf-8"?>
<resources>

<style name="Theme.SplashScreenTutorial"
parent="android:Theme.Material.Light.NoActionBar" />

</resources>

2) We need to make a new values-v31 theme:
  • Add a new folder 'values-v31' to SplashScreenTutorial\app\src\main\res 
  • Browse to this folder and add a new themes.xml. You may need to do this in file explorer or something similar.
    • I simply copy the values\themes.xml and then delete the contents.
  • Add the following to the values-31\themes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.App.Starting"
parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">
@color/black</item>
<item name="postSplashScreenTheme">
@style/Theme.SplashScreenTutorial</item>
<item name="android:windowSplashScreenAnimatedIcon">
@drawable/animated_outline_trophy_24</item>
</style>
</resources>

3) Now link it in the original values\theme
<?xml version="1.0" encoding="utf-8"?>
<resources>

<style name="Theme.SplashScreenTutorial"
parent="android:Theme.Material.Light.NoActionBar" />

<style name="Theme.App.Starting"
parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">
@color/black</item>
<item name="postSplashScreenTheme">
@style/Theme.SplashScreenTutorial</item>
</style>
</resources>

4) We need to link our theme in the manifest
  • For the activity, replace the SplashScreenTutorial theme with App.Starting.

<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.App.Starting">
<intent-filter>

5) In your Main Activity, add installSplashScreen() after onCreate (before super):

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
SplashScreenTutorialTheme {

And that's all you need to get your animation working! If you run your app, you should get an ugly animation (we'll tweak it after). 

Note: You'll need to terminate the app completely to get this to play. Closing and running it again will just resume it so no loading animation.


Clearly, we have a little work yet to do.

Fixing and Improvements

1) Let's quickly fix the size because this is annoying.
  • Very simply, add some scaling to the outline_trophy_24.xml (scaleX and Y)
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp" android:tint="#87DE5D"
android:viewportHeight="960"
android:viewportWidth="960"
android:width="24dp">

<group
android:name="animationGroup"
android:pivotX="480"
android:pivotY="480"
android:scaleX="0.75"
android:scaleY="0.75">

<path android:fillColor="@android:color/white"
android:pathData="M280,840L280,760L440,760L440,636Q391,
625 352.5,594.5Q314,564 296,518Q221,509 170.5,452.5Q120,
396 120,320L120,280Q120,247 143.5,223.5Q167,200 200,
200L280,200L280,120L680,120L680,200L760,200Q793,
200 816.5,223.5Q840,247 840,280L840,320Q840,
396 789.5,452.5Q739,509 664,518Q646,564 607.5,
594.5Q569,625 520,636L520,760L680,760L680,
840L280,840ZM280,432L280,280L200,280L200,320Q200,
358 222,388.5Q244,419 280,432ZM480,560Q530,560 565,
525Q600,490 600,440L600,200L360,200L360,440Q360,490 395,
525Q430,560 480,560ZM680,432Q716,419 738,388.5Q760,
358 760,320L760,280L680,280L680,432ZM480,380Q480,380 480,
380Q480,380 480,380L480,380L480,380L480,380Q480,
380 480,380Q480,380 480,380Z"/>
</group>
</vector>

  • As we are here, we can also change the launcher app to match
    • In Res, just add a new Image Asset and replace the Forground Layer with Trophy 24 (scaled to fit) and change the background.
      • I've set mine to black.
    • I've also removed the extra group information that was copied across
    • Here's my full ic_launcher_foreground and how it appears on the device
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="#87DE5D">
<group android:scaleX="0.50"
android:scaleY="0.50"
android:translateX="240"
android:translateY="240">

<path android:fillColor="@android:color/white"
android:pathData="M280,840L280,760L440,760L440,636Q391,
625 352.5,594.5Q314,564 296,518Q221,509 170.5,452.5Q120,
396 120,320L120,280Q120,247 143.5,223.5Q167,200 200,
200L280,200L280,120L680,120L680,200L760,200Q793,
200 816.5,223.5Q840,247 840,280L840,320Q840,
396 789.5,452.5Q739,509 664,518Q646,564 607.5,
594.5Q569,625 520,636L520,760L680,760L680,
840L280,840ZM280,432L280,280L200,280L200,320Q200,
358 222,388.5Q244,419 280,432ZM480,560Q530,560 565,
525Q600,490 600,440L600,200L360,200L360,440Q360,490 395,
525Q430,560 480,560ZM680,432Q716,419 738,388.5Q760,
358 760,320L760,280L680,280L680,432ZM480,380Q480,380 480,
380Q480,380 480,380L480,380L480,380L480,380Q480,
380 480,380Q480,380 480,380Z"/>
</group>
</vector>


So next we'll see the final animation:

Staged Animation

We can also add stages to the animation and allow the full animation to play.

1) Let's ensure the animation completes by adding a delay.
  • I've added keepOnScreen and a delay before switching it to false.
    • For testing purposes, I've set this to 1250ms which is slightly over the recommended limit. You would likely want to keep this to 1000 or below for real apps to avoid frustrating your users.

class MainActivity : ComponentActivity() {
private var keepOnScreen = true
override fun onCreate(savedInstanceState: Bundle?) {
val splash = installSplashScreen()
super.onCreate(savedInstanceState)
splash.setKeepOnScreenCondition { keepOnScreen }
lifecycleScope.launch {
// 1000ms is the maximum recommended animation length
delay(1250) // a little over for this test
keepOnScreen = false
}
enableEdgeToEdge()
setContent {

2) Next, we can make a fancier animation. First, let's make a dimens in our Res/values

<resources>
<item name="scale_gone_value" type="dimen">0.0</item>
<item name="scale_full_value" type="dimen">0.4</item>
<item name="scale_oversize_value" type="dimen">0.6</item>
</resources>
3) Now we can create our new animation using these values without repeating the same numbers everywhere.
  • Copy splash_animator.xml and name it splash_animator_sequence.xml
  • Note the use of the following tags:
    • set 
    • ordering="sequentially
    • objectAnimator
    • propertyValuesHolder
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially">

<!-- Stage 1 - 300ms -->
<objectAnimator
android:duration="300">

<propertyValuesHolder
android:propertyName="scaleX"
android:valueType="floatType"
android:valueFrom="@dimen/scale_half_value"
android:valueTo="@dimen/scale_full_value" />

<propertyValuesHolder
android:propertyName="scaleY"
android:valueType="floatType"
android:valueFrom="@dimen/scale_half_value"
android:valueTo="@dimen/scale_full_value" />

</objectAnimator>

<!-- Stage 2 - 1000ms - too long for real app -->
<objectAnimator
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="720"
android:duration="1000"
android:valueType="floatType"/>

<!-- Stage 3 - 300 ms - total 1600ms-->
<objectAnimator
android:duration="300">

<propertyValuesHolder
android:propertyName="scaleX"
android:valueType="floatType"
android:valueFrom="@dimen/scale_full_value"
android:valueTo="@dimen/scale_gone_value" />

<propertyValuesHolder
android:propertyName="scaleY"
android:valueType="floatType"
android:valueFrom="@dimen/scale_full_value"
android:valueTo="@dimen/scale_gone_value" />

</objectAnimator>

</set>

4) To make sure this displays properly, we need to make few more small changes:
  • Change the animated_outline_trophy_24.xml to use the new animation
    <target
android:animation="@animator/splash_animator_sequence"
android:name="animationGroup" />

  • Change the timing on the Main Activity
lifecycleScope.launch {
// 1000ms is the maximum recommended animation length
delay(1500) // a little over for this test
keepOnScreen = false
}
And that should produce this horrible spinning monstrosity!

Bonus - Tweaked Animations with Interpolators

Animated GIF example

You can further control the animation by using your own Interpolators. I have put a list of these at the end of the tutorial for convenience. As a very brief example of this, we can add an inertia type effect:

1) In res, create an anim folder (res/anim). In that, create overshoot_interpolator.xml.
  • Note: We've added a tension change here to so it doesn't rotate too much after the original animation finishes.
<?xml version="1.0" encoding="utf-8"?>
<overshootInterpolator
xmlns:android="http://schemas.android.com/apk/res/android"
android:tension="1.0"/>

2) Make a new animator (in res/animator) called splash_overshoot_sequence.xml.

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially">

<objectAnimator
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="720"
android:duration="1000"
android:valueType="floatType"
android:interpolator="@anim/overshoot_interpolator"/>

<objectAnimator
android:duration="300"
android:startOffset="300">

<propertyValuesHolder
android:propertyName="scaleX"
android:valueType="floatType"
android:valueFrom="@dimen/scale_oversize_value"
android:valueTo="@dimen/scale_gone_value" />

<propertyValuesHolder
android:propertyName="scaleY"
android:valueType="floatType"
android:valueFrom="@dimen/scale_oversize_value"
android:valueTo="@dimen/scale_gone_value" />

</objectAnimator>

</set>

3) In res/drawable, create animated_outline_overshoot_24.xml
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/outline_trophy_24">

<target
android:animation="@animator/splash_overshoot_sequence"
android:name="animationGroup" />

</animated-vector>

4) Hopefully, you remember how to do this part but I'll repeat it anyway. In your v31 theme, change the animation to animated_outline_overshoot_24.xml.
<?xml version="1.0" encoding="utf-8"?>
<resources> ...
<item name="android:windowSplashScreenAnimatedIcon">
@drawable/animated_outline_overshoot_24</item>
...

After running the change app, you should now get this slightly more sensible animation. Notice how  it does the double spin and then goes a little bit over with the inertia.


Summary

We've looked at importing a standing icon and then adding animation and using it as a splash screen. We looked at creating a series of actions as well as adding interpolators to adjust how the animation appears.

We did not look at animating specific parts of an image. This page on Splash Screens gives a good example of this.

Reference Links

Android Developer Official Link - Splash Screen
PL Coding Youtube Videos
Interpolator class
AccelerateDecelerateInterpolator
AccelerateInterpolator
AnticipateInterpolator
AnticipateOvershootInterpolator
BounceInterpolator
CycleInterpolator
DecelerateInterpolator
LinearInterpolator
OvershootInterpolator


Comments

Popular Posts