Look Deep Into ViewPager2

Earlier this year, Google announced ViewPager2 alpha-04 in Google IO/19. ViewPager2 is the replacement of ViewPager and is much better. The biggest change in the ViewPager2 API is that it now uses the RecyclerView. First of all, you need to migrate to AndroidX because ViewPager2 is not supported in the android.support library. In this article, I won’t go into AndroidX migration too deeply but just to say/mention I didn’t encounter any big issues during migration even in the big scale project. To migrate to AndroidX you can do it with Android Studio easily by clicking Refactor menu and then select Migrate to AndroidX. For further details go to the official developer’s Android site https://developer.android.com/jetpack/androidx/migrate
What’s new with ViewPager2
As I mentioned above, the most important point is that ViewPager2 is built on RecyclerView component. In this way, you can use the RecyclerView.Adapter with VP2 in your Adapter class. RecyclerView is so robust and brings a lot of advantages and it also has modifiable fragment collections and more.
These changes below are the reason to choose ViewPager2 over ViewPager. Let’s take a closer look at what’s changed with ViewPager2:
- ViewPager2 is based on the RecyclerView
- Right-to-left (RTL) layout support
- Allows vertical paging that means LayoutManager support
- Improved dataset change notifications
- Ability to programmatically scroll ViewPager2 (fakeDragBy(offsetPx))
- MarginPageTransformer introduced to provide an ability to create space between pages (outside of page inset).
- CompositePageTransformer introduced to combine multiple Page Transformers
- Implicit usage of getCurrentItem() and setCurrentItem() methods
- Diff Util support since RecyclerView has part of ViewPager2
- ItemDecorator introduced with behavior consistent with RecyclerView
API changes
- FragmentStateAdapter replaces FragmentStatePagerAdapter
- RecyclerView.Adapter replaces PagerAdapter
- registerOnPageChangeCallback replaces the addPageChangeListener()
Google Devs have done a good job easing the migration from ViewPager to ViewPager2 and keeping an almost 100% compatible API.
Deeper Understanding of ViewPager2
When I was looking into ViewPager2 source code, I discovered that ViewPager2 is a final class, which means it can’t be extended. So it not possible create CustomViewPager class. ViewPager2 is basically extended from ViewGroup. I was curious and began to compare ViewPager2 and ViewPager source code and realized that ViewPager is almost 3000 lines of code whereas ViewPager2 currently has almost 1500 lines so ViewPager2 has less magic because they are based on recyclerView and so ViewPager2 implicitly uses RecyclerView which means there is no big difference with the RecyclerView.
In the Constructor method in VP2, there is a method called initialize().
Google developers slightly modified RecyclerView to take advantage of accessibility, scroll control and also initialized LinearLayoutManager, via modified LinearLayoutManager to adjust accessibility when user scrolling is disabled. After all, the LayoutManager object should be set to RecyclerView.
setOrientation() method defined within viewPager. ViewPager2 has vertical and horizontal support, horizontal is set as a default.
You might already use PagerSnapHelper with RecyclerView. PagerSnapHelper can help achieve similar behavior to ViewPager so PagerSnapHelper is attached to RecyclerView within ViewPager2.
Step By Step Implementation of ViewPager2
- ViewPager2 definition in Gradle. Currently, the latest version is beta02 so double-check for the latest version with https://developer.android.com/jetpack/androidx/releases/viewpager2
dependencies {
implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'}
2. After sync the Gradle, we can use ViewPager2 🎉. Now we have ViewPager2 in the project. Define the layout file:
<?xml version="1.0" encoding="utf-8"?>
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/viewPager2"
android:layout_width="match_parent"
android:layout_height="match_parent" />
3. Just to show a simple sample I have opted to create a dummy list and set items in the adapter.
val categories = listOf(
Category(1, "Your Recording"),
Category(2, "Film"),
Category(3, "Series"),
Category(4, "Kids"),
Category(5, "Sport")
)
4. Let’s create an adapter, it has the same usage as the RecyclerView.Adapter
class CategoryAdapter : RecyclerView.Adapter<CategoryViewHolder>() {
var list: List<Category> = listOf()override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder {
return CategoryViewHolder(parent)
}override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
holder.bind(list[position])
}fun setItem(list: List<Category>) {
this.list = list
notifyDataSetChanged()
}override fun getItemCount(): Int = list.size
}
ViewHolder is below:
class CategoryViewHolder constructor(itemView: View) :
RecyclerView.ViewHolder(itemView) {
constructor(parent: ViewGroup) :
this(LayoutInflater.from(parent.context).inflate(R.layout.category_item, parent, false))fun bind(category: Category) {
itemView.categoryName.text = category.name
}
}
I have demonstrated a simple ViewHolder which just shows a name category in the list item but you can add more elements depending on your needs.
5. Set Adapter to viewPager2
viewPager2.adapter = CategoryAdapter()
adapter.setItem(categories)
6. Final Step to Seeing the Result
In addition so that, ViewPager2 brings easy to use the new feature which adjusts the orientation by yourself in code. If you need to use Vertical Orientation in ViewPager2, just enough to set the orientation to Vertical.
viewPager2.orientation = ViewPager2.ORIENTATION_VERTICAL
So far, The feeling is the same as when we implement the RecyclerView as per this sample.
Using OnPageChangeCallback()
Other pros of ViewPager2 are OnPageChangeCallback methods which are onPageSelected(), onPageScrollStateChanged() and onPageScrolled() . But, Viewpager has addOnPageChangeListener callback interface thus we had to override all methods even though we need just one in all.
With the ViewPager2, you don’t need to override all of them, because OnPageChangeCallback is an abstract static class and no boilerplate and only desired method(s) can be used.
To use OnPageChangeCallback, firstly you need to register it.
viewPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {//override method(s) what you need it})
onPageSelected: This method will be invoked when a new page becomes selected
onPageScrollStateChange: Called when the scroll state changes
onPageScrolled: This method will be invoked when the current page is scrolled
Let’s imagine, when you select a page to listen to this event, you need to implement OnPageSelected() method.
class ViewPager2PageChangeCallback(private val listener: (Int) -> Unit) : ViewPager2.OnPageChangeCallback() {override fun onPageSelected(position: Int) {
super.onPageSelected(position)
when (position) {
0 -> listener.invoke(5)
}
}
}
When you register ViewPager2PageChangeCallback, you can get the position whenever a new page is selected.
viewPager2PageChangeCallback = ViewPager2PageChangeCallback {
viewPager2.setCurrentItem(it, false)
}
viewPager2.registerOnPageChangeCallback(viewPager2PageChangeCallback)
setCurrentItem() have required index and smoothScroll parameters, however, smoothScroll is optional. We can use one of 2 methods to set the current item.
Either
public void setCurrentItem(int item, boolean smoothScroll) { }
or
public void setCurrentItem(int item) { }
and Don’t forget to unregister the OnPageChangeCallback.
override fun onDestroy() {
super.onDestroy()
viewPager2.unregisterOnPageChangeCallback(viewPager2PageChangeCallback)
}
Using PageTransformer, MarginPageTransformer, and CompositePageTransformer
Enrich View the Page with animation by using PageTransformer.
Making animation is super easy and more efficient with the help of PageTransformer, MarginPageTransformer, and CompositePageTransformer. You can easily create custom animations with PageTransformer as you are seeing here
class ViewPager2PageTransformation : ViewPager2.PageTransformer {override fun transformPage(page: View, position: Float) {
when {
position < -1 ->
page.alpha = 0.1f
position <= 1 -> {
page.alpha = Math.max(0.2f, 1 - Math.abs(position))
}
else -> page.alpha = 0.1f
}
}
}
Also, We can adorn the animation by applying translation the x and y-axes of the page.
class ViewPager2PageTransformation : ViewPager2.PageTransformer {override fun transformPage(page: View, position: Float) {
val absPos = Math.abs(position)
page.apply {
translationY = absPos * 500f
translationX = absPos * 500f
scaleX = 1f
scaleY = 1f
}when {
position < -1 ->page.alpha = 0.1f
position <= 1 -> {
page.alpha = Math.max(0.2f, 1 - Math.abs(position))
}
else -> page.alpha = 0.1f
}
}
}
After all, ViewPager2 need to set your Custom PageTransformer
viewPager2.setPageTransformer(ViewPager2PageTransformation())
In that way, you provide a simple transformer in pages or create the desired custom animation.
Moreover, If you need to add space between pages, just use MarginPageTransformer(marginPx) which is also a new within ViewPager2.
Please keep in mind; marginPx cannot be a negative value, otherwise, you will get a crash. Let’s say the first transformer is needed to add space between pages.
val marginPageTransformer = MarginPageTransformer(50)
and other transformers can take care of the page translation in our animation
ViewPager2.PageTransformer { page, position ->page.apply {
translationY = Math.abs(position) * 500f
scaleX = 1f
scaleY = 1f
}
}
CompositePageTransformer(as the name indicates itself), if you have several page transformers, CompositePageTransformer allows combining of all transformers.
viewPager2.setPageTransformer(CompositePageTransformer().also {
it.addTransformer(marginPageTransformer)
it.addTransformer(translationPageTransformer())
})
Result is here
Besides, I would like to extend the use case with a useful example that I have in mind with using new additional features inside VP2
I believe that the most popular use case using animation for pages, achieve multiple items visible in one page, displaying indicator and so on. Before VP2, you might prefer to use Recycler view with PageSnapHelper, because to achieve some cases more easily with them. Due to applying implicitly LayoutManager, PageSnapHelper to RecyclerView.
How to achieve showing multiple pages at the same time via ViewPager2
Before VP2, we were showing multiple pages with using pageWeight() in ViewPager or we should use Recycler View for that. Since VP2 supports neither pageWeight() nor negative margin value, one of the ways to solve the problem is using PageTransformer(). Before entering the example, let us remind you why we need the following methods…
Firstly, you can still use your existing ViewPager configuration for VP2 as well
- clipToPadding: true to clip children to the padding of the group, and resize (but not clip) any EdgeEffect to the padded region. False otherwise
- ClipChild : true to clip children to their bounds, false otherwise
- OffScreenPageLimit : How many pages will be kept offscreen on either side. Valid values are all values {@code >= 1} and OFFSCREEN_PAGE_LIMIT_DEFAULT which is -1
Previous alpha versions of ViewPager2 had bugs, one of them clipToPadding() which did not work as we expected, but this issue has already been fixed by Google with the latest version.
with(viewPager2) {
clipToPadding = false
clipChildren = false
offscreenPageLimit = 3
}
Then, to configure offset between pages
- Define pageMargin and peekOffset in dimen.xml
<dimen name="pageMargin">20dp</dimen>
<dimen name="offset">30dp</dimen>
- Set margin and offset with via using PageTransformer()
val pageMarginPx = resources.getDimensionPixelOffset(R.dimen.pageMargin)
val offsetPx = resources.getDimensionPixelOffset(R.dimen.offset)
viewPager2.setPageTransformer { page, position ->
val viewPager = page.parent.parent as ViewPager2
val offset = position * -(2 * offsetPx + pageMarginPx)
if (viewPager.orientation == ORIENTATION_HORIZONTAL) {
if (ViewCompat.getLayoutDirection(viewPager) == ViewCompat.LAYOUT_DIRECTION_RTL) {
page.translationX = -offset
} else {
page.translationX = offset
}
} else {
page.translationY = offset
}
}
- Our pages still need to set offset and margin which are using the above configuration
<dimen name="pageMarginAndOffset">50dp</dimen>
set pageMarginAndOffset to Page
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="216dp"
android:layout_marginLeft="@dimen/pageMarginAndOffset"
android:layout_marginRight="@dimen/pageMarginAndOffset" />
By this configuration, now we have more visible items in one screen via ViewPager2.
Final Result is here:

To Sum up, Google devs have quite a speed release process for VP2, On June 5, 2019; Google released alpha-05 version. In this version, some of the bugs had already been fixed plus it comes with a lot of new features, huge improvements, and optimized source code by Google.
After a short time, Beta01 was released but this version contains an unintentional dependency because of jacoco.agent. However, You can use the beta02 version which has already been released.
We have taken advantage of Viewpager2 in our existing projects so far, and with the examples, I mentioned above, I have tried to explain the main parts that come to my mind first. You can easily use all the advantages of Viewpager2 in your project(s) as well and hopefully, it will give you peace of mind and you will be satisfied, choose VP2 to replace VP and to get rid of the unnecessary custom implementations.
That’s all so far. Thanks for reading the article.
If you would like to see more details, check out my Github link: https://github.com/serapbercin/androidx-ViewPager2
ViewPager2 has known Issues, to follow these issues click the link below
https://issuetracker.google.com/issues?q=viewpager2
Reference Links:
ViewPager2 | Android Developers
https://developer.android.com/jetpack/androidx/releases/viewpager2