ViewPager Tutorial: Getting Started in Kotlin

In this ViewPager tutorial for Android, you’ll learn how to use a ViewPager to navigate between content pages in Kotlin. By Diana Pislaru.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Connecting the PagerAdapter and the ViewPager

Open MainActivity.kt and add the following line at the top to declare your MoviesPagerAdapter:

private lateinit var pagerAdapter: MoviesPagerAdapter

Next add the following inside onCreate(), beneath the existing code:

pagerAdapter = MoviesPagerAdapter(supportFragmentManager, movies)
viewPager.adapter = pagerAdapter

This initializes your MoviesPagerAdapter and connects it to the ViewPager

Note: supportFragmentManager is equivalent to the getSupportFragmentManager() method you would use in Java and viewPager.adapter = pagerAdapter is the same as viewPager.setAdapter(pagerAdapter). Read more about getters and setters in Kotlin here.

Build and run. The app should behave like the original version, but you can now navigate between movies by swiping rather than pressing buttons :].

Swiping ViewPager

Note: Using the FragmentStatePagerAdapter saves you from having to deal with saving the current page across a runtime configuration change, like rotating the device. The state of the Activity is usually lost in those situations and you would have to save it in the Bundle object passed as a parameter in onCreate(savedInstanceState: Bundle?). Luckily, the PagerAdapter you used does all the work for you. You can read more about the savedInstanceState object and the Activity lifecycle here.

Endless Scrolling

A nice feature you often see is being able to swipe continuously between pages in a circular manner. That is going to the last page when swiping right on the first one and going to the first one when swiping left on the last. For example, swiping between 3 pages would look like this: 

Page1 -> Page2 -> Page3 -> Page1 -> Page2

Page2

The FragmentStatePagerAdapter will stop creating new fragments when the current index reaches the number of objects returned by getCount(), so you need to change the method to return a fairly large number that the users are not very likely to reach by continuously swiping in the same direction. That way the PagerAdapter will keep creating pages until the page index reaches the value returned by getCount().

Open MoviesPagerAdapter.kt and create a new constant representing the large number by adding this line at the top of the file above the class definition:

private const val MAX_VALUE = 200

Now replace the return movies.size line inside getCount() with this:

return movies.size * MAX_VALUE

By multiplying the length of the array with MAX_VALUE, the swipe limit will grow proportionally to the number of movies in your list. This way you don’t have to worry about getCount() returning a number that is less than the number of movies as your movie list grows.

The only problem you now have is inside the Adapter’s getItem(position: Int) method. Since getCount() now returns a number larger than the size of the list, the ViewPager will try to access the movie at an index greater than the array size when the user swipes past the last movie.

Replace the code inside getItem(position: Int) with this line:

return MovieFragment.newInstance(movies[position % movies.size])

This will ensure that the ViewPager doesn’t request the element at an index larger than movies.size because the remainder after you divide the position by movies.size will always be greater than or equal to 0 and less than movies.size.

Right now the infinite scrolling works only when the user navigates forward through the array (swipes left). That is because, when your app starts, the ViewPager displays the movie at index 0. To fix this issue, open MainActivity.kt and add the following line inside onCreate() below the line where you connect the PageAdapter to the ViewPager

viewPager.currentItem = pagerAdapter.count / 2

This tells the ViewPager to display the movie found in the middle of the array. The user has now plenty of swiping to do in either direction before they reach an end. To ensure that the movie displayed at the beginning will still be the first one in your list, set MAX_VALUE to be an even number (in this case 200 works fine). This way, after you divide pagerAdapter.count by 2, pagerAdapter.count % movies.size = 0 (which is the first index that the ViewPager asks for when the app starts).

Build and run. You should now be able to swipe left and right a decent amount of times and the movies will start again from the beginning after you reach the last one and from the end when you reach the first one.

Endless scroll

Adding Tabs

A TabLayout is a nice feature that makes it easy to explore and switch between pages. The TabLayout contains a tab for each page, which usually displays the page title. The user can tap on a tab to navigate directly to the desired page or can use a swipe gesture over the TabLayout to switch between pages.

If you try to add a TabLayout to your ViewPager you won’t be able to see any tabs because the layout will be automatically populated with as many tabs as the FragmentStatePagerAdapter tells it by calling the getCount() method, which now returns a pretty large number. Trying to fit that many tabs on your screen will make them really narrow.

Luckily, there is a third party library called RecyclerTabLayout that solves this problem. The library uses the RecyclerView in its implementation. You can learn more about the mysterious RecyclerView from this tutorial. To install the library, open up build.grade (Module: app) and add the following line inside dependencies:

implementation 'com.nshmura:recyclertablayout:1.5.0'

The recyclertablayout library uses an old version of the Android Support Libraries, so you’ll need to add the following to make the Gradle sync happy:

implementation 'com.android.support:recyclerview-v7:26.1.0'

Tap Sync Now on the yellow pop-up and wait until Android Studio installs the library.

Open activity_main.xml and paste the following snippet above the ViewPager:

<com.nshmura.recyclertablayout.RecyclerTabLayout
    android:id="@+id/recyclerTabLayout"
    android:layout_height="@dimen/tabs_height"
    android:layout_width="match_parent" />

Now add the following property to your ViewPager to align it below the RecyclerTabLayout:

android:layout_below="@id/recyclerTabLayout"

Your whole layout file should now look like this:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_height="match_parent"
                android:layout_width="match_parent"
                tools:context="com.raywenderlich.favoritemovies.MainActivity">

  <com.nshmura.recyclertablayout.RecyclerTabLayout
      android:id="@+id/recyclerTabLayout"
      android:layout_height="@dimen/tabs_height"
      android:layout_width="match_parent" />

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

</RelativeLayout>

Open MainActivity.kt and import RecyclerTabLayout at the top of the file, like this:

import com.nshmura.recyclertablayout.RecyclerTabLayout

Now add the following at the top of the class to declare a RecyclerTabLayout instance:

private lateinit var recyclerTabLayout: RecyclerTabLayout

Add this block of code inside onCreate(), above the line where you set viewPager.currentItem:

recyclerTabLayout = findViewById(R.id.recyclerTabLayout)
recyclerTabLayout.setUpWithViewPager(viewPager)

The first line connects your RecyclerTabLayout instance to the xml view and the second one links the RecyclerTabLayout to your ViewPager.

The last thing you have to do is let the RecyclerTabLayout know what titles to display on the Tabs. Open MoviesPagerAdapter.kt and add the following method inside the class:

override fun getPageTitle(position: Int): CharSequence {
  return movies[position % movies.size].title
}

This method tells the TabLayout what to write on the tab placed at a particular position. It returns the title of the movie that corresponds with the fragment created inside getItem(position: Int).

Run the app. You should be able to see the tabs changing as you swipe through the pages. Try tapping on a tab and see how the ViewPager will scroll automatically to the corresponding movie :].

Tabs

Diana Pislaru

Contributors

Diana Pislaru

Author

Jason Donmoyer

Tech Editor

Odie Edo-Osagie

Final Pass Editor

Joe Howard

Team Lead

Over 300 content creators. Join our team.