The ViewPager class has finally gotten an improved version known as ViewPager2. This newer version offers enhanced functionality and addresses commondifficulties faced while using the ViewPager class.

This tutorial will explore examples of ViewPager2, helping you learn how to use this component using practical step by step examples.

You should consider migrating to ViewPager2 as it is the version that is now prefered and will continue receiving active development and updates. It is also more powerful than ViewPager.

Here are some features of ViewPager2:

Kotlin Android SharedPreferences

Kotlin Android SharedPreferences
Kotlin Android SharedPreferences

Vertical orientation support

Unlike ViewPager which needs complex customization to achieve vertical paging, ViewPager2 provides this capability natively. It also of course provides horizontal paging. Enabling vertical or horizontal paging is as easy as it gets, all you need to do is provide the value for the android:orientation attribute in your xml layout:

<androidx.viewpager2.widget.ViewPager2
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"       
    android:orientation="vertical" />

You can also set this attribute programmatically using the setOrientation().

Right-to-left support

Based on the locale your app is being used in, you may want to support RTL(right-to-left) paging. Unlike ViewPager, ViewPager2 provides this capability natively and in fact automatically without any action from your part. This is done based on the users locale in the device.

However you can also set it manually in your layout as follows:

<androidx.viewpager2.widget.ViewPager2
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layoutDirection="rtl" />

or programmatically using the setLayoutDirection() method.

Modifiable fragment collections

The collections assigned to ViewPager2 as the data source can be mutated. You do this by calling the notifyDatasetChanged(). This method updates the UI when that collection changes or is modified.

The advantage of this is that you can mutate or change your collection of fragments at runtime and ViewPager2 will refresh itself based on that without requiring rebinding.

DiffUtil

ViewPager2 also, unlike ViewPager supports DiffUtil. This is because it is built from RecyclerView. This not only makes the binding of fragments more efficient but also implies that ViewPager2 can natively take advantage of dataset change animations from the RecyclerView class.

Let’s look at some examples.

Kotlin Android ViewPager2 Example

Step by step example on how to use ViewPager2 in Kotlin Android.

Step 1: Dependencies

Start by adding the ViewPager2 androidx dependency. We will also use a third party CircularIndicator to indicate the pages:

    // CircleIndicator
    implementation 'me.relex:circleindicator:2.1.6'

    // ViewPager2
    implementation "androidx.viewpager2:viewpager2:1.0.0"

Step 2: Design Layout

We need two layouts. The first is a layout to represent a single page. Remember we will be swiping pages. This layout defines a single page:

item_page.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="TextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <RadioGroup
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" >

        <RadioButton
            android:id="@+id/radioButton4"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="RadioButton" />

        <RadioButton
            android:id="@+id/radioButton3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="RadioButton" />

        <RadioButton
            android:id="@+id/radioButton2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="RadioButton" />

        <RadioButton
            android:id="@+id/radioButton"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="RadioButton" />
    </RadioGroup>

</androidx.constraintlayout.widget.ConstraintLayout>

Then another layout tobe inflated into the main activity. This is where you add the ViewPager2 as well as the CircleIndicator:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginBottom="16dp"
        android:text="قبلی"
        app:layout_constraintBottom_toTopOf="@+id/indicator"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/btn_next"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="16dp"
        android:text="بعدی"
        app:layout_constraintBottom_toTopOf="@+id/indicator"
        app:layout_constraintEnd_toEndOf="parent" />

    <me.relex.circleindicator.CircleIndicator3
        android:id="@+id/indicator"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:background="#1e88e5"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_pager2"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/indicator"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" >

    </androidx.viewpager2.widget.ViewPager2>

</androidx.constraintlayout.widget.ConstraintLayout>

Step 3: Create ViewPager2 Adapter

Because as we had said earlier ViewPager2 is built on RecyclerView, it needs an adapter. But not a PagerAdapter like ViewPager, instead a RecyclerView.Adapter:

Here is an example of such an adapter:

ViewPagerAdapter

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView

class ViewPagerAdapter(private val dataValue: List<String>, private val condition: ConditionViewPager) :
    RecyclerView.Adapter<ViewPagerAdapter.ViewPagerViewHolder>() {

    inner class ViewPagerViewHolder(view: View) : RecyclerView.ViewHolder(view) {

        //val txt: TextView = view.findViewById(R.id.textView)

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewPagerViewHolder =
        ViewPagerViewHolder(
            LayoutInflater.from(parent.context).inflate(R.layout.item_page, parent, false)
        )

    override fun onBindViewHolder(holder: ViewPagerViewHolder, position: Int) {
        condition.condition(position,dataValue.size)
    }

    override fun getItemCount(): Int = dataValue.size

    interface ConditionViewPager {

        fun condition(position : Int, fullSize : Int)

    }

}

Step 4: Write Activity Code

The next step is to write the main activity code. This is where you will connect the ViewPager2 with the CircleIndicator:

MainActivity.kt

package com.alirezabashi98.viewpager

import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.viewpager2.widget.ViewPager2
import me.relex.circleindicator.CircleIndicator3

class MainActivity : AppCompatActivity(), ViewPagerAdapter.ConditionViewPager {

    private val data = mutableListOf<String>()
    private lateinit var viewPager: ViewPager2
    private lateinit var indicator: CircleIndicator3
    private lateinit var btnNext: Button
    private lateinit var btnBack: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        castView()
        addToList()

        viewPager.adapter = ViewPagerAdapter(data, this)
        viewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL

        indicator.setViewPager(viewPager)

        btnNext.setOnClickListener {
            viewPager.apply {
                beginFakeDrag()
                fakeDragBy(-10f)
                endFakeDrag()
            }
        }

        btnBack.setOnClickListener {
            viewPager.apply {
                beginFakeDrag()
                fakeDragBy(10f)
                endFakeDrag()
            }
        }

    }

    private fun castView() {

        viewPager = findViewById(R.id.view_pager2)
        indicator = findViewById(R.id.indicator)
        btnNext = findViewById(R.id.btn_next)
        btnBack = findViewById(R.id.btn_back)

    }

    private fun addToList() {
        for (item in 1..20) {
            data.add("item $item")
        }
    }

    override fun condition(position: Int, fullSize: Int) {

        if (position == fullSize) {
            btnNext.text = "پایان"
        }
        Toast.makeText(this, "$position / $fullSize", Toast.LENGTH_SHORT).show()

    }

}

Reference

Here are the reference links:

Number Link
1. Download code
2. Follow code author
3. Read more about ViewPager2