A simple onboarding screens using ViewPager in Android

The layouts
activity_main.xml, the layout for main screen of the app.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

activity_onboarding.xml, the layout for the onboarding activity which contains only a ViewPager for hosting fragments.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

fragment_onboarding.xml, the layout for individual onboarding screen.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:text="One"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_next"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="20dp"
        android:background="?selectableItemBackgroundBorderless"
        android:text="Next"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>

SharedPref.kt, this class retrieves and updates a flag variable in SharedPreference to identifiy if the app was on a first launch.

import android.app.Activity
import android.content.Context
import android.content.SharedPreferences

class SharedPref private constructor() {

    fun setIsFirstLaunchToFalse() {
        editor!!.putBoolean(IS_FIRST_LAUNCH, false)
        editor!!.commit()
    }

    fun isFirstLaunch() : Boolean {
        return sharedPreferences!!.getBoolean(IS_FIRST_LAUNCH, true)
    }

    companion object {
        private val sharedPref = SharedPref()
        private var sharedPreferences: SharedPreferences? = null
        private var editor: SharedPreferences.Editor? = null
        private val IS_FIRST_LAUNCH = "is_first_launch"

        @Synchronized
        fun getInstance(context: Context): SharedPref {

            if (sharedPreferences == null) {
                sharedPreferences = context.getSharedPreferences(context.packageName, Activity.MODE_PRIVATE)
                editor = sharedPreferences!!.edit()
            }

            return sharedPref
        }
    }
}

OnBoardingFragment.kt, the view for individual onboarding screen.

import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_onboarding.*

class OnBoardingFragment : Fragment() {

    interface OnBoardingListener {
        fun onNextClick()
    }

    companion object {
        val NAME = "name"

        /**
         * @param name a string to be displayed on the fragment
         * @param listener click listener to pass click events to the activity
         */
        fun newInstance(name : String, listener : OnBoardingListener) : Fragment {
            val fragment = OnBoardingFragment()
            val bundle = Bundle()
            bundle.putString(NAME, name)
            fragment.arguments = bundle
            fragment.onBoardingListener = listener
            return fragment
        }
    }

    private lateinit var onBoardingListener : OnBoardingListener

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

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        tv_name.text = arguments.getString(NAME, "")

        // pass the click event to the activity
        tv_next.setOnClickListener({
            onBoardingListener.onNextClick()
        })
    }
}

OnBoardingViewPagerAdapter.kt, the adapter for managing each onboarding screen/fragment in the ViewPager.

import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentStatePagerAdapter

class OnBoardingViewPagerAdapter(fm : FragmentManager) : FragmentStatePagerAdapter(fm) {

    var fragments = ArrayList<Fragment>()

    fun addFragments(fragments : ArrayList<Fragment>) {
        this.fragments = fragments
    }

    override fun getItem(position: Int): Fragment {
        return fragments[position]
    }

    override fun getCount(): Int {
        return fragments.size
    }

}

OnBoardingActivity.kt, the activity that controls the onboarding screen flow. Each onboarding screen is a fragment in the ViewPager. The variable currentPage tracks the current onboarding screen. If the user is on the last onboarding screen and then taps next, the main activity should be launched.

import android.content.Intent
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v4.view.ViewPager
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_onboarding.*

class OnBoardingActivity : AppCompatActivity(), OnBoardingFragment.OnBoardingListener {

    private var currentPage = 0
    private val fragments = ArrayList<Fragment>()


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_onboarding)


        val adapter = OnBoardingViewPagerAdapter(supportFragmentManager)
        for (i in 0..5) {
            fragments.add(OnBoardingFragment.newInstance(i.toString(), this))
        }
        adapter.addFragments(fragments)
        view_pager.adapter = adapter

        // keep track of the current screen
        view_pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
            override fun onPageScrollStateChanged(state: Int) {
            }

            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
            }

            override fun onPageSelected(position: Int) {
                currentPage = position
            }
        })
    }

    // open the main screen when next is tapped on the last screen
    override fun onNextClick() {
        if (currentPage == fragments.size - 1) {
            startActivity(Intent(this, MainActivity::class.java))
            SharedPref.getInstance(applicationContext).setIsFirstLaunchToFalse()
            finish()
        } else {
            view_pager.setCurrentItem(currentPage + 1, true)
        }
    }
}

MainActivity.kt, the main screen of the app, this redirect to the onboarding screen if it is a first app launch.

import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // launch the onboarding screen if it is the first launch of the app
        if (isFirstLaunch()) {
            startActivity(Intent(this, OnBoardingActivity::class.java))
            finish()
        }
    }

    private fun isFirstLaunch() : Boolean {
        return SharedPref.getInstance(applicationContext).isFirstLaunch()
    }
}

Style for removing the actionbar on the onboarding activity

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name = "NoActionBar" parent = "AppTheme">
        <item name = "windowActionBar">false</item>
        <item name = "windowNoTitle">true</item>
    </style>
</resources>

Then in the mainifest file, use the NoActionBar for the OnBoardingActivity

<activity android:name=".OnBoardingActivity" android:theme="@style/NoActionBar"/>

Complete project in Github

Search within Codexpedia

Custom Search

Search the entire web

Custom Search