Migrate from ViewPager to ViewPager2

ViewPager2 is an improved version of the ViewPager library that offers enhanced functionality and addresses common difficulties with using ViewPager. If your app already uses ViewPager, read this page to learn more about migrating to ViewPager2.

If you want to use ViewPager2 in your app and are not currently using ViewPager, read Slide between fragments using ViewPager2 and Create swipe views with tabs using ViewPager2 for more information.

Benefits of migrating to ViewPager2

The primary reason to migrate is that ViewPager2 is receiving active development support and ViewPager is not. However, ViewPager2 also offers several other specific advantages.

Vertical orientation support

ViewPager2 supports vertical paging in addition to traditional horizontal paging. You can enable vertical paging for a ViewPager2 element by setting its android:orientation attribute:

<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() method.

Right-to-left support

ViewPager2 supports right-to-left (RTL) paging. RTL paging is enabled automatically where appropriate based on locale, but you can also manually enable RTL paging for a ViewPager2 element by setting its android:layoutDirection attribute:

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

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

Modifiable fragment collections

ViewPager2 supports paging through a modifiable collection of fragments, calling notifyDatasetChanged() to update the UI when the underlying collection changes.

This means that your app can dynamically modify the fragment collection at runtime, and ViewPager2 will correctly display the modified collection.

DiffUtil

ViewPager2 is built on RecyclerView, which means it has access to the DiffUtil utility class. This results in several benefits, but most notably it means that ViewPager2 objects natively take advantage of the dataset change animations from the RecyclerView class.

Migrate your app to ViewPager2

Follow these steps to update ViewPager objects in your app to ViewPager2:

Update XML layout files

First, replace the ViewPager elements in your XML layout files with ViewPager2 elements:

<!-- A ViewPager element -->
<android.support.v4.view.ViewPager
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

<!-- A ViewPager2 element -->
<androidx.viewpager2.widget.ViewPager2
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Update adapter classes

When using ViewPager, you had to extend the adapter class that supplied new pages to the object. Depending on the use case, ViewPager used three different abstract classes. ViewPager2 only uses two abstract classes.

For each ViewPager object you are converting to a ViewPager2 object, update the adapter class to extend the appropriate abstract class as follows:

Constructor parameters

Fragment-based adapter classes inheriting from FragmentPagerAdapter or FragmentStatePagerAdapter always accept a single FragmentManager object as a constructor parameter. When you extend FragmentStateAdapter for a ViewPager2 adapter class, you have the following options for constructor parameters instead:

  • The FragmentActivity object or Fragment object where the ViewPager2 object resides. In most cases, this is the better option.
  • A FragmentManager object and a Lifecycle object.

View-based adapter classes inheriting directly from RecyclerView.Adapter do not require a constructor parameter.

Override methods

Your adapter classes also need to override different methods for ViewPager2 than they did for ViewPager:

  • Instead of getCount(), override getItemCount(). Other than the name, this method is unchanged.
  • Instead of getItem(), override createFragment() in fragment-based adapter classes. Make sure that your new createFragment() method always supplies a new fragment instance each time the function is called instead of reusing instances.

Summary

In summary, to convert a ViewPager adapter class for use with ViewPager2, you must make the following changes:

  1. Change the superclass to RecyclerView.Adapter for paging through views, or FragmentStateAdapter for paging through fragments.
  2. Change the constructor parameters in fragment-based adapter classes.
  3. Override getItemCount() instead of getCount().
  4. Override createFragment() instead of getItem() in fragment-based adapter classes.

Kotlin

// A simple ViewPager adapter class for paging through fragments
class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
    override fun getCount(): Int = NUM_PAGES

    override fun getItem(position: Int): Fragment = ScreenSlidePageFragment()
}

// An equivalent ViewPager2 adapter class
class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
    override fun getItemCount(): Int = NUM_PAGES

    override fun createFragment(position: Int): Fragment = ScreenSlidePageFragment()
}

Java

// A simple ViewPager adapter class for paging through fragments
public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
    public ScreenSlidePagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        return new ScreenSlidePageFragment();
    }

    @Override
    public int getCount() {
        return NUM_PAGES;
    }
}

// An equivalent ViewPager2 adapter class
private class ScreenSlidePagerAdapter extends FragmentStateAdapter {
    public ScreenSlidePagerAdapter(FragmentActivity fa) {
        super(fa);
    }

    @Override
    public Fragment createFragment(int position) {
        return new ScreenSlidePageFragment();
    }

    @Override
    public int getItemCount() {
        return NUM_PAGES;
    }
}

Refactor TabLayout interfaces

ViewPager2 introduces changes to TabLayout integration. If you currently use a ViewPager with a TabLayout object to display horizontal tabs for navigation, you need to refactor the TabLayout object for integration with ViewPager2.

TabLayout has been decoupled from ViewPager2 and is now available as part of Material components. This means that in order to use it, you need to add the appropriate dependency to your build.gradle file:

Groovy

implementation "com.google.android.material:material:1.1.0-beta01"

Kotlin

implementation("com.google.android.material:material:1.1.0-beta01")

You also need to change the TabLayout element's location in the hierarchy of your XML layout file. With ViewPager, the TabLayout element is declared as a child of the ViewPager element; but with ViewPager2, the TabLayout element is declared directly above the ViewPager2 element, on the same level:

<!-- A ViewPager element with a TabLayout -->
<androidx.viewpager.widget.ViewPager
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</androidx.viewpager.widget.ViewPager>

<!-- A ViewPager2 element with a TabLayout -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

</LinearLayout>

Finally, you must update the code that attaches the TabLayout object to the ViewPager object. While TabLayout uses its own setupWithViewPager() method to integrate with ViewPager, it requires a TabLayoutMediator instance to integrate with ViewPager2.

The TabLayoutMediator object also handles the task of generating page titles for the TabLayout object, which means that the adapter class does not need to override getPageTitle():

Kotlin

// Integrating TabLayout with ViewPager
class CollectionDemoFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val tabLayout = view.findViewById(R.id.tab_layout)
        tabLayout.setupWithViewPager(viewPager)
    }
    ...
}

class DemoCollectionPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {

    override fun getCount(): Int  = 4

    override fun getPageTitle(position: Int): CharSequence {
        return "OBJECT ${(position + 1)}"
    }
    ...
}

// Integrating TabLayout with ViewPager2
class CollectionDemoFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val tabLayout = view.findViewById(R.id.tab_layout)
        TabLayoutMediator(tabLayout, viewPager) { tab, position ->
            tab.text = "OBJECT ${(position + 1)}"
        }.attach()
    }
    ...
}

Java

// Integrating TabLayout with ViewPager
public class CollectionDemoFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        TabLayout tabLayout = view.findViewById(R.id.tab_layout);
        tabLayout.setupWithViewPager(viewPager);
    }
    ...
}

public class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {
    ...
    @Override
    public int getCount() {
        return 4;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return "OBJECT " + (position + 1);
    }
    ...
}

// Integrating TabLayout with ViewPager2
public class CollectionDemoFragment : Fragment() {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        TabLayout tabLayout = view.findViewById(R.id.tab_layout);
        new TabLayoutMediator(tabLayout, viewPager,
                (tab, position) -> tab.setText("OBJECT " + (position + 1))
        ).attach();
    }
    ...
}

Support nested scrollable elements

ViewPager2 does not natively support nested scroll views in cases where the scroll view has the same orientation as the ViewPager2 object that contains it. For example, scrolling would not work for a vertical scroll view inside a vertically-oriented ViewPager2 object.

To support a scroll view inside a ViewPager2 object with the same orientation, you must call requestDisallowInterceptTouchEvent() on the ViewPager2 object when you expect to scroll the nested element instead. The ViewPager2 nested scrolling sample demonstrates one way of solving this problem with a versatile custom wrapper layout.

Additional resources

To learn more about ViewPager2, see the following additional resources.

Samples

Videos