AppIntro (OnBoard) Screen - Kotlin

Introduction:

App Intro/Onboard screen is mainly to display the key features of our app to users while launching it, in a sweet way. Let's do that simply with TabLayout, ViewPager and its PageTransformer. 

Design:

slide_page.xml 

Desgin this layout with ViewPager and TabLayout to begin with.

<?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:id="@+id/introLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/tabLayout" />


    <android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/next"
        app:tabBackground="@drawable/tab_selector"
        app:tabGravity="center"
        app:tabIndicatorHeight="0dp" />

    <TextView
        android:id="@+id/next"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_margin="15dp"
        android:text="@string/next"
        android:textAppearance="@android:style/TextAppearance.Medium"
        android:textColor="@color/colorPrimary" />


    <TextView
        android:id="@+id/skip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_margin="15dp"
        android:text="@string/skip"
        android:textAppearance="@android:style/TextAppearance.Medium"
        android:textColor="@color/colorPrimary" />


</RelativeLayout>

intro_layout.xml 

The layout have the design that will be displayed on each page of Viewpager.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/third_layout"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/description"
        android:text="@string/title"
        android:gravity="center"
        android:layout_margin="15dp"
        android:textColor="#000000"
        android:textSize="24sp"/>

    <TextView
        android:id="@+id/description"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="@string/description"
        android:gravity="center"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:layout_marginBottom="15dp"
        android:textSize="18sp"/>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/title"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="5dp">

    <ImageView
        android:id="@+id/introImage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/cycling"/>

        <ImageView
            android:id="@+id/iv_bike_shadow"
            android:layout_width="80dp"
            android:layout_height="8dp"
            android:layout_centerHorizontal="true"
            android:layout_below="@+id/introImage"
            android:layout_marginTop="30dp"
            android:src="@drawable/shadow"/>
    </RelativeLayout>
</RelativeLayout>

Drawable Files:

tab_selector.xml:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/selected_dot" android:state_selected="true" />
    <item android:drawable="@drawable/default_dot" />
</selector>

selected_dot.xml:


<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape
            android:innerRadius="0dp"
            android:shape="ring"
            android:thickness="4dp"
            android:useLevel="false">
            <solid android:color="@color/colorPrimary"/>
        </shape>
    </item>
</layer-list>

default_dot.xml:


<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape
            android:innerRadius="0dp"
            android:shape="ring"
            android:thickness="4dp"
            android:useLevel="false">

            <solid android:color="@color/divider"/>
        </shape>
    </item>
</layer-list>

shadow.xml:


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

Values Files:

strings.xml:

Include the following in your strings.xml file.

    <string name="next">Next</string>
    <string name="skip">Skip</string>
    <string name="continues">Continue</string>
    <string name="skip_pressed">Skip Pressed!!</string>
    <string name="description">How great is it to travel? 
      To meet new people, see new places, experience different cultures.</string>
    <string name="title">Travel World</string>

colors.xml:

Include the following in your colors.xml


<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="transparentBlack">#48000000</color>
<color name="grey">#C0C0C0</color>
<color name="white">#ffffff</color>
<color name="divider">#e6e6e6</color>

Code:

AppIntroScreen.kt

In this Activity, we have to initialize TabLayout with Viewpager. Also we have to set the adapter to the ViewPager. Here, we have extended FragmentPagerAdapter for Adapter class. Then we have to set our custom PageTransformer to the ViewPager to perform animation while scrolling the pages.

class AppIntroScreen : AppCompatActivity(), View.OnClickListener {
    
    val mResources = intArrayOf(R.drawable.cycling, R.drawable.airplane, R.drawable.kick_scooter, R.drawable.firefighter)
    lateinit var adapter: SlidingPagerAdapter
    var currentTab = 0
    var tabCount = 0
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.slide_page)

        tabCount = mResources.size
        adapter = SlidingPagerAdapter(supportFragmentManager, mResources)
        viewPager.adapter = adapter


        val pageTransformer = ParallaxTransformer()
        viewPager.setPageTransformer(true, pageTransformer)


        tabLayout.setupWithViewPager(viewPager)
        tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabReselected(tab: TabLayout.Tab?) {
            }

            override fun onTabUnselected(tab: TabLayout.Tab?) {
            }

            override fun onTabSelected(tab: TabLayout.Tab?) {
                viewPager.currentItem = tab!!.position
            }

        })
        viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
            override fun onPageScrollStateChanged(state: Int) {

            }

            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
                currentTab = position + 1
                if (currentTab == tabCount) {
                    skip.text = getString(R.string.continues)
                } else {
                    skip.text = getString(R.string.skip)
                }
            }

            override fun onPageSelected(position: Int) {
            }

        })
        next.setOnClickListener(this)
        skip.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.next -> {
                if (currentTab == tabCount) {
                    skip.text = getString(R.string.continues)
                } else {
                    skip.text = getString(R.string.skip)
                    viewPager.currentItem = currentTab
                }
            }

            R.id.skip -> {
                Toast.makeText(this@AppIntroScreen, getString(R.string.skip_pressed), Toast.LENGTH_SHORT).show()
                // Proceed to Main/Home Activity of the App
            }
        }
    }
}

class SlidingPagerAdapter(fragmentManager: FragmentManager?, val mResources: IntArray) : FragmentPagerAdapter(fragmentManager) {

    override fun getItem(position: Int): Fragment {
        return IntroPage().newInstance(position)
    }
    
    override fun getCount(): Int {
        return mResources.size
    }
}

IntroPage.kt (Fragment)

In this Fragment class, we have to change the images, based on the position of the ViewPager's Adapter.

class IntroPage : Fragment() {

    var position = 0
    val mResources = intArrayOf(R.drawable.cycling, R.drawable.airplane, R.drawable.kick_scooter, R.drawable.firefighter)

    fun newInstance(position: Int): IntroPage {
        val fragment = IntroPage()
        val arguments = Bundle()
        arguments.putInt("POSITION", position)
        fragment.setArguments(arguments)
        return fragment
    }

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

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

        val args = arguments
        position = args.getInt("POSITION")


        introImage.setImageDrawable(resources.getDrawable(mResources[position]))

    }
}

ParallaxTransformer.kt

Here, we can play with animation of our choice.


class ParallaxTransformer : ViewPager.PageTransformer {
    override fun transformPage(view: View, position: Float) {
        val absPosition = Math.abs(position)
        if (position < -1) { 
            // This page is way off-screen to the left.
            view.alpha = 1f
        } else if (position <= 1) {
            val image = view.findViewById<ImageView>(R.id.introImage)
            image?.apply {
                setScaleX(1.0f - absPosition * 2)
                setScaleY(1.0f - absPosition * 2)
                setAlpha(1.0f - absPosition * 2)
            }
            val shadow = view.findViewById<ImageView>(R.id.shadow)
            shadow?.apply {
                setScaleX(1.0f - absPosition * 2)
                setScaleY(1.0f - absPosition * 2)
                setAlpha(1.0f - absPosition * 2)
            }
        } else {
            // This page is way off-screen to the right.
            view.alpha = 1f
        }
    }
}

Run Application:

Now our Application is ready to launch . Output screen will be like,





Comments

  1. I am getting an error with the View.onClickListener at the top of the class.

    ReplyDelete
    Replies
    1. Have you imported android.view.View?? Also kindly check whether you used OnClickListener or onClickListener..

      Delete

Post a Comment

Popular posts from this blog

SOAP Client using ksoap2 in Android - Kotlin

RecyclerView with different number of columns using SpanSizeLookup

Using Camera in Android - Kotlin

Databinding in RecyclerView - Android - Kotlin

Map, Location update and AutoComplete Places - Kotlin

Room with LiveData, ViewModel - Android Architecture Components - Kotlin

Stripe Integration in Android - Kotlin

Braintree Integration in Android - Kotlin

Exploring Android Slices - JetPack

Android JetPack - Scheduling Tasks with WorkManager