Setup ViewPager2 for Android (Kotlin)

June 18, 2019

Depedencies

dependencies {
    // https://mvnrepository.com/artifact/androidx.viewpager2/viewpager2
    implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha05'
}

Layout

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

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

</androidx.constraintlayout.widget.ConstraintLayout>

Activity + ViewModel

  • Support Horizontal/Vertical Scrolling: viewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
  • Observe page changed: viewPager.registerOnPageChangeCallback
  • Scroll to page programatically: viewPager.currentItem = PAGE or viewPager.setCurrentItem(position, true) (with Animation)
  • Disable user scroll: viewPager.isUserInputEnabled = false
class TestViewPagerActivity : AppCompatActivity() {

    companion object {
        @JvmStatic
        fun newInstance(context: Context) =
            Intent(context, TestViewPagerActivity::class.java).apply {

            }
    }

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

        init()
    }

    private fun init() {
        val viewModel = ViewModelProviders.of(this).get(TestViewPageViewModel::class.java)

        val adapter = LocalAdapter(viewModel)
        val items = listOf(
            Item("Title 1", "Content 1"),
            Item("Title 2", "Content 2"),
            Item("Title 3", "Content 3")
        )
        adapter.replaceItems(items)
        viewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
        viewPager.adapter = adapter
        // disable scrolling
        // viewPager.isUserInputEnabled = false
        // viewPager.currentItem = 1
        viewPager.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() {
            override fun onPageSelected(position: Int) {
                super.onPageSelected(position)

                Timber.d("page=$position")
            }
        })

        viewModel.nextPageEvent.observe(this, Observer { position ->
            viewPager.setCurrentItem(position, true)
        })

    }

    class Item(val title: String, val content: String)

    class LocalAdapter(val viewModel: TestViewPageViewModel) : RecyclerView.Adapter<LocalAdapter.ViewHolder>() {
        private var items = listOf<Item>()

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LocalAdapter.ViewHolder {
            val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.test_viewpage_item, parent, false)
            return ViewHolder(view)
        }

        override fun getItemCount(): Int = items.size

        override fun onBindViewHolder(holder: LocalAdapter.ViewHolder, position: Int) {
            val item = items[position]

            holder.titleTextView.text  = item.title
            holder.contentTextView.text = item.content

            if (position < itemCount - 1) {
                holder.nextButton.isEnabled = true
                holder.nextButton.setOnClickListener {
                    // next
                    viewModel.nextPageEvent.value = position + 1
                }
            }
            else {
                holder.nextButton.isEnabled = false
            }
        }

        fun replaceItems(items: List<Item>) {
            this.items = items
            notifyDataSetChanged()
        }

        inner class ViewHolder(override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer
    }
}

test_viewpage_item layout

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        app:cardUseCompatPadding="true"
        app:cardCornerRadius="12dp"
        android:layout_margin="8dp">

    <LinearLayout android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_gravity="center"
                  android:orientation="vertical"
                  android:padding="8dp">

        <TextView
                android:id="@+id/titleTextView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textAppearance="@style/TextAppearance.AppCompat.Large"
                android:textAlignment="center"
                tools:text="Title"
        />

        <TextView
                android:id="@+id/contentTextView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textAlignment="center"
                tools:text="Content"
        />

        <Button
                android:id="@+id/nextButton"
                android:text="Next"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>


    </LinearLayout>
</androidx.cardview.widget.CardView>
class TestViewPageViewModel: ViewModel() {
    internal val nextPageEvent = SingleLiveEvent<Int>()
}

NOTE: Source for SingleLiveEvent.

References:

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.