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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
ViewPager Tutorial: Getting Started in Kotlin
20 mins
Creating a Custom FragmentStatePagerAdapter
In the project navigator pane, right-click on com.raywenderlich.favoritemovies and select New -> Kotlin File/Class. Name it MoviesPagerAdapter and select Class for Kind. Hit OK.
Replace the contents of this file with the following:
package com.raywenderlich.favoritemovies
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentStatePagerAdapter
// 1
class MoviesPagerAdapter(fragmentManager: FragmentManager, private val movies: ArrayList<Movie>) :
FragmentStatePagerAdapter(fragmentManager) {
// 2
override fun getItem(position: Int): Fragment {
return MovieFragment.newInstance(movies[position])
}
// 3
override fun getCount(): Int {
return movies.size
}
}
Let’s go over this step-by-step.
- Your new class extends
FragmentStatePagerAdapter
. The constructor of the superclass requires aFragmentManager
, thus your customPagerAdapter
needs it as well. You also need to provide the list of movies as a parameter. - Return the fragment associated with the object located at the specified position.
- Return the number of objects in the array.
When the ViewPager
needs to display a fragment, it initiates a chat with the PagerAdapter
. First, the ViewPager
asks the PagerAdapter
how many movies are in the array by calling getCount()
. Then it will call getItem(int position)
whenever a new page is about to be visible. Within this method, the PagerAdapter
creates a new fragment that displays the information about the movie at the correct position in the array.
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.
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 :].
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.
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.