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:

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:
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.

Where to go next?
You can download this full code from the link below:
Be sure to check my other cool posts here about:
- Android TabLayout and ViewPager Tutorial
- Bottom Navigation Bar Android Using Kotlin
- How to Get Data from REST API using Retrofit
- Android Take Photo from Camera and Gallery
- Android Simple Image Gallery Tutorial
I hope you like my post, comment and share it with love!
Thank you for this good tutorial. Keep bringing more like this 👍
unfortunately the slides always open and not only at the first start of the app