Android Intro Slider and Splash Screen Tutorial

It’s time for me to show you how to create an Android intro slider and splash screen. An Intro screen is a good way to tell users what your app is all about or to showcase the key features of the app. The splash screen is a screen that is displayed from the first time app launches. It’s usually only displayed for a couple of seconds.

The intro screen that we want to build here in this post is gonna use a new Android widget, it’s the Android ViewPager2. The ViewPager2 is a successor of the old ViewPager that solves some issues ViewPager has and it also has some new and improved features that are cool. I’ll explain it later in this post.

What will the app look like?

The app will look like this after you finish this tutorial:

Android Intro Slider and Splash Screen Tutorial

What will you learn?

  • Know how to create a simple and basic splash screen.
  • Understand how to create an introduction screen that contains a slider.
  • Understand how to implement a ViewPager2 and FragmentStateAdapter to build an Android intro slider.
  • Creating a custom IndicatorLayout widget for giving a nice dot view on our intro screen. I will show you in this post.

Note: In this post, I used Android Studio 3.5.3, make sure you use the latest Android Studio, or if you already install it, be sure to check the latest update. The Kotlin version that I used is Kotlin 1.3.61.

Getting Started – Android Intro Slider and Splash Screen 

Open your Android Studio and choose to Start a new Android Studio Project. Then set the Application Name SimpleIntroSlider and select Kotlin as the language. Give the Activity Name MainActivity and wait until the Android Studio finishes preparing your project.

Open app/build.gradle file and add the following codes below. Add a ViewPager2 dependency inside dependencies block code:

dependencies {
   ...

   implementation "androidx.viewpager2:viewpager2:1.0.0"

   ...
}

That dependency library allows you to use the Android ViewPager2 to display the list of Fragment that you will need later when creating the slider. Next, click Sync Now to begin downloading the required dependencies.

Preparing the Image Assets

Download this image assets from this link below:

Download the Image Assets

After you download it, extract the file and copy and paste the contents into the res/drawable directory. It contains a logo for the splash screen and some icons for the Android intro slider.

Creating the Splash Screen

Create a new empty Activity, the first one is SplashActivity.kt and please also add an XML layout inside the res/layout directory, the activity_splash.xml file.

Put this code inside the activity_splash.xml.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="#4a555d">

   <ImageView
       android:layout_width="250dp"
       android:layout_height="250dp"
       android:src="@drawable/splash_logo"
       android:layout_centerInParent="true"
       />

</RelativeLayout>

Next, put this code inside the SplashActivity.kt file.

package com.thesimplycoder.simpleintroslider

import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.view.View
import androidx.appcompat.app.AppCompatActivity

class SplashActivity : AppCompatActivity() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)

       // making the status bar transparent
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ) {
           window.decorView.systemUiVisibility =
               View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
       }

       setContentView(R.layout.activity_splash)

       Handler().postDelayed(object: Runnable {
           override fun run() {
               startActivity(Intent(this@SplashActivity, MainActivity::class.java))
               finish()
           }
       }, 2000)
   }
}

The Handler().postDelayed() method is for delaying the action given after a certain time. In this case, I put a 2000 delay which means it will trigger the action inside the run() code block after 2000 milliseconds or 2 seconds.

Add a new styles.xml file inside res/values-v21 directory.

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
       <item name="colorPrimary">@color/colorPrimary</item>
       <item name="colorPrimaryDark">@android:color/transparent</item>
       <item name="colorAccent">@color/colorAccent</item>
   </style>
</resources>

This custom style is only for the Android device version above 21 or Lollipop. It will set the color of the status bar to become transparent.

Next, open AndroidManifest.xml and make sure your code looks like this:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.thesimplycoder.simpleintroslider">

   <application
       android:allowBackup="true"
       android:icon="@mipmap/ic_launcher"
       android:label="@string/app_name"
       android:roundIcon="@mipmap/ic_launcher_round"
       android:supportsRtl="true"
       android:theme="@style/AppTheme">
       <activity android:name=".SplashActivity">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />

               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
       </activity>
            
       <activity android:name=".MainActivity"/>
   </application>

</manifest>

You must place the android.intent.action.MAIN and android.intent.category.LAUNCHER inside the SplashActivity code block or tag to set that it is the first activity the will be launched when the app opens.

Then try to run your app and see what happens. The splash screen will launch the first, and after two seconds, it will display the MainActivity screen.

Implementing the Intro Slider

You have done the splash screen part, now is the time for the intro slider screen. But before we create the intro screen, we need to create its indicator for the slider. It will look like this:

Creating a custom IndicatorLayout

I will show you how to create a dot-type indicator like that on Android. So I’m gonna think that a custom widget that inherits from the Android LinearLayout should do just fine. So how do you implement that? Hmm, let’s get started!

First, create a new XML file inside the res/values directory named attrs.xml.

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <declare-styleable name="IndicatorLayout">
       <attr name="indicatorCount" format="integer"/>
   </declare-styleable>
</resources>

The <declare-stylable> is for declaring a custom styleable that will be used on the XML layout when using the custom widget. It is some kind of attribute or argument parameter for a custom widget. The <attr> is a tag where you declare the attribute name and the data format. For our IndicatorLayout widget later we will only use one attribute and that is indicatorCount which accepts an integer type value.

Second, create two new XML drawable resources inside res/drawable directory:

The indicator_unselected.xml file:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
   android:shape="oval">
   <solid android:color="#80FFFFFF"/>
</shape>

The indicator_selected.xml file:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
   android:shape="oval">
   <solid android:color="#FFFFFF"/>
</shape>

The drawable resources above are for the indicator background that you will need later to give a dot-like indicator. The shape should be oval because you will make a dot indicator, so the other types will not be suitable for it.

Third, create a new Kotlin class file named IndicatorLayout.kt.

package com.thesimplycoder.simpleintroslider

import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.LinearLayout

class IndicatorLayout : LinearLayout {

   private var indicatorCount: Int = 0
   private var selectedPosition: Int = 0

   constructor(context: Context): super(context)

   constructor(context: Context, attrs: AttributeSet): super(context, attrs) {
       initIndicators(context, attrs, 0)
   }

   constructor(
       context: Context, attrs: AttributeSet, defStyleAttr: Int
   ): super(context, attrs, defStyleAttr) {
       initIndicators(context, attrs, defStyleAttr)
   }

   private fun initIndicators(context: Context, attrs: AttributeSet, defStyleAttr: Int) {
       val typedArray = context.obtainStyledAttributes(
           attrs, R.styleable.IndicatorLayout, defStyleAttr, 0)

       try {
           indicatorCount = typedArray.getInt(R.styleable.IndicatorLayout_indicatorCount, 0)
       } finally {
           typedArray.recycle()
       }

       updateIndicators()
   }

   private fun px(dpValue: Float): Int {
       return (dpValue * context.resources.displayMetrics.density).toInt()
   }

   private fun updateIndicators() {
       removeAllViews()

       for (i in 0 until indicatorCount) {
           val indicator = View(context)

           // setting indicator layout margin
           val layoutParams = LayoutParams(px(10f), px(10f))
           layoutParams.setMargins(px(3f), px(3f), px(3f), px(3f))
           indicator.layoutParams = layoutParams

           indicator.setBackgroundResource(R.drawable.indicator_unselected)

           // add the view to indicator layout
           addView(indicator)
       }
   }

   fun setIndicatorCount(count: Int) {
       indicatorCount = count
       updateIndicators()
   }

   fun selectCurrentPosition(position: Int) {
       if (position >= 0 && position <= indicatorCount) {

           selectedPosition = position

           for (index in 0 until indicatorCount) {
               val indicator = getChildAt(index)

               if (index == selectedPosition) {
                   indicator.setBackgroundResource(R.drawable.indicator_selected)
               } else {
                   indicator.setBackgroundResource(R.drawable.indicator_unselected)
               }
           }
       }
   }
}

So here is the IndicatorLayout class that inherits from LinearLayout. This class will take the attribute parameter from our declared attribute on attrs.xml via TypedArray. You can see it inside the initIndicators() method. The logic of this class is just as simple as this, it accepts how many the indicator count and it will add the View as many as the indicator count inside the layout, see updateIndicators() method. There is also a selectCurrentPosition(position: Int) method to toggle which indicator that is currently selected.

Implementing ViewPager2 and its Adapter

Create a new Kotlin class file, IntroSliderAdapter.kt:

package com.thesimplycoder.simpleintroslider

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter

class IntroSliderAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {

   private val fragmentList = ArrayList<Fragment>()

   override fun getItemCount(): Int {
       return fragmentList.size
   }

   override fun createFragment(position: Int): Fragment {
       return fragmentList.get(position)
   }

   fun setFragmentList(list: List<Fragment>) {
       fragmentList.clear()
       fragmentList.addAll(list)
       notifyDataSetChanged()
   }
}

The ViewPager2 will need a FragmentStateAdapter as its data source manager. Next, create an empty Activity the IntroSliderActivity.kt both with its XML layout activity_intro_slider.xml.

Replace the code inside the activity_intro_slider.xml.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="@color/colorPrimary">

   <androidx.viewpager2.widget.ViewPager2
       android:id="@+id/vpIntroSlider"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       />

   <com.thesimplycoder.simpleintroslider.IndicatorLayout
       android:id="@+id/indicatorLayout"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="horizontal"
       android:layout_alignParentBottom="true"
       android:layout_marginBottom="80dp"
       android:layout_marginStart="30dp"
       android:layout_marginEnd="30dp"
       android:gravity="center_horizontal"
       app:indicatorCount="3"
       />

   <TextView
       android:id="@+id/tvSkip"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Skip"
       android:textAllCaps="true"
       android:textColor="@android:color/white"
       android:textSize="16sp"
       android:layout_alignParentBottom="true"
       android:layout_alignParentStart="true"
       android:padding="8dp"
       android:layout_margin="16dp"
       android:clickable="true"
       android:background="?selectableItemBackground"
       />

   <TextView
       android:id="@+id/tvNext"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Next"
       android:textAllCaps="true"
       android:textColor="@android:color/white"
       android:textSize="16sp"
       android:layout_alignParentBottom="true"
       android:layout_alignParentEnd="true"
       android:padding="8dp"
       android:layout_margin="16dp"
       android:clickable="true"
       android:background="?selectableItemBackground"
       />

</RelativeLayout>

The layout above is using our fresh created custom IndicatorLayout. As you can see, I put the app:indicatorCount as we declare in attrs.xml that accepts integer value.

Open and replace the code inside the IntroSliderActivity.kt.

package com.thesimplycoder.simpleintroslider

import android.content.Intent
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.viewpager2.widget.ViewPager2
import kotlinx.android.synthetic.main.activity_intro_slider.*

class IntroSliderActivity : AppCompatActivity() {

   private val fragmentList = ArrayList<Fragment>()

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)

       // making the status bar transparent
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ) {
           window.decorView.systemUiVisibility =
               View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
       }

       setContentView(R.layout.activity_intro_slider)

       val adapter = IntroSliderAdapter(this)
       vpIntroSlider.adapter = adapter

       fragmentList.addAll(listOf(
           Intro1Fragment(), Intro2Fragment(), Intro3Fragment()
       ))
       adapter.setFragmentList(fragmentList)

       indicatorLayout.setIndicatorCount(adapter.itemCount)
       indicatorLayout.selectCurrentPosition(0)

       registerListeners()
   }

   private fun registerListeners() {
       vpIntroSlider.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {

           override fun onPageSelected(position: Int) {
               indicatorLayout.selectCurrentPosition(position)

               if (position < fragmentList.lastIndex) {
                   tvSkip.visibility = View.VISIBLE
                   tvNext.text = "Next"
               } else {
                   tvSkip.visibility = View.GONE
                   tvNext.text = "Get Started"
               }
           }
       })

       tvSkip.setOnClickListener {
           startActivity(Intent(this, MainActivity::class.java))
           finish()
       }

       tvNext.setOnClickListener {
           val position = vpIntroSlider.currentItem

           if (position < fragmentList.lastIndex) {
               vpIntroSlider.currentItem = position + 1
           } else {
               startActivity(Intent(this, MainActivity::class.java))
               finish()
           }
       }
   }
}

This activity is pretty straightforward I think. It just initializes the IntroSliderAdapter and the ViewPager2 will use it as its adapter. The IntroSliderAdapter will receive a List containing three Fragments for each of its slider content. Next, create three new Fragments for the ViewPager2 content.

Create this following Fragment and XML layout resource files:

The fragment_intro1.xml file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:padding="30dp"
   android:gravity="center"
   android:background="#056FAF">

   <ImageView
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:src="@drawable/ic_airplane"
       />

   <TextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/text_intro1"
       android:textColor="@android:color/white"
       android:textSize="24sp"
       android:gravity="center"
       android:layout_marginTop="20dp"
       android:textStyle="bold"
       />

   <TextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/desc_intro1"
       android:textColor="@android:color/white"
       android:textSize="16sp"
       android:gravity="center"
       android:layout_marginTop="20dp"
       />

</LinearLayout>

The Intro1Fragment.kt kotlin class file:

package com.thesimplycoder.simpleintroslider

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment

class Intro1Fragment : Fragment() {

   override fun onCreateView(
       inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
   ): View? {
       return inflater.inflate(R.layout.fragment_intro1, container, false)
   }
}

The fragment_intro2.xml file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:padding="30dp"
   android:gravity="center"
   android:background="#F57C00">

   <ImageView
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:src="@drawable/ic_room"
       />

   <TextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/text_intro2"
       android:textColor="@android:color/white"
       android:textSize="24sp"
       android:gravity="center"
       android:layout_marginTop="20dp"
       android:textStyle="bold"
       />

   <TextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/desc_intro2"
       android:textColor="@android:color/white"
       android:textSize="16sp"
       android:gravity="center"
       android:layout_marginTop="20dp"
       />

</LinearLayout>

The Intro2Fragment.kt kotlin class file:

package com.thesimplycoder.simpleintroslider

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment

class Intro2Fragment : Fragment() {

   override fun onCreateView(
       inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
   ): View? {
       return inflater.inflate(R.layout.fragment_intro2, container, false)
   }
}

The fragment_intro3.xml file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:padding="30dp"
   android:gravity="center"
   android:background="#D81B60">

   <ImageView
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:src="@drawable/ic_beach"
       />

   <TextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/text_intro3"
       android:textColor="@android:color/white"
       android:textSize="24sp"
       android:gravity="center"
       android:layout_marginTop="20dp"
       android:textStyle="bold"
       />

   <TextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/desc_intro3"
       android:textColor="@android:color/white"
       android:textSize="16sp"
       android:gravity="center"
       android:layout_marginTop="20dp"
       />

</LinearLayout>

The Intro3Fragment.kt kotlin class file:

package com.thesimplycoder.simpleintroslider

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment

class Intro3Fragment : Fragment() {

   override fun onCreateView(
       inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
   ): View? {
       return inflater.inflate(R.layout.fragment_intro3, container, false)
   }
}

After that, please open the strings.xml inside the res/values directory and add the following string value:

<resources>
   <string name="app_name">Simple Intro Slider</string>

   <string name="text_intro1">Travel Everywhere</string>
   <string name="text_intro2">Best Room Rates</string>
   <string name="text_intro3">Exotic Destinations</string>

   <string name="desc_intro1">Enjoy flight to any destination with so many discounts and promotions</string>
   <string name="desc_intro2">Book your favorite hotel room with hassle-free and reasonable rates</string>
   <string name="desc_intro3">Enjoy your beautiful moments to exotic places around the world</string>
</resources>

Modify the SplashActivity.kt code on Handler.postDelayed() call to start the IntroSliderActivity.

Handler().postDelayed(object: Runnable {
   override fun run() {
       startActivity(Intent(this@SplashActivity, IntroSliderActivity::class.java))
       finish()
   }
}, 2000)

The last this that you’re gonna do is register the IntroSliderActivity in AndroidManifest.xml like this:

<application
   android:allowBackup="true"
   android:icon="@mipmap/ic_launcher"
   android:label="@string/app_name"
   android:roundIcon="@mipmap/ic_launcher_round"
   android:supportsRtl="true"
   android:theme="@style/AppTheme">
   ...

   <activity android:name=".IntroSliderActivity"/>

   ...
</application>

It’s done now, you can run and enjoy your app and see what your app can do.

Vertical Slider Intro Screen

If you want a different style of the slider and it is a vertical slide, you can modify the orientation of ViewPager2 on the activity_intro_slider.xml to vertical.

<androidx.viewpager2.widget.ViewPager2
       android:id="@+id/vpIntroSlider"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="vertical"
       />

When you run the app, you can swipe the slider up and down vertically.

Android Intro Slider and Splash Screen vertical slider

Where to go next?

You can download this full code from the link below:

Download final code

Be sure to check my other cool posts here about:

I hope you like my post, comment and share it with love!

You may also like...

5 Responses

  1. K.a says:

    Thank you for this good tutorial. Keep bringing more like this 👍

  2. Victor says:

    unfortunately the slides always open and not only at the first start of the app

  1. March 1, 2020

    […] How to put an Intro Slider and Splash Screen to your app […]

  2. March 12, 2020

    […] Android Intro Slider and Splash Screen Tutorial […]

  3. June 14, 2020

    […] Android Intro Slider and Splash Screen Tutorial […]

Join the discussion...

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: