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:
- When
ViewPagerusedPagerAdapterto page through views, useRecyclerView.AdapterwithViewPager2. - When
ViewPagerusedFragmentPagerAdapterto page through a small, fixed number of fragments, useFragmentStateAdapterwithViewPager2. - When
ViewPagerusedFragmentStatePagerAdapterto page through a large or unknown number of fragment, useFragmentStateAdapterwithViewPager2.
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
FragmentActivityobject orFragmentobject where theViewPager2object resides. In most cases, this is the better option. - A
FragmentManagerobject and aLifecycleobject.
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(), overridegetItemCount(). Other than the name, this method is unchanged. - Instead of
getItem(), overridecreateFragment()in fragment-based adapter classes. Make sure that your newcreateFragment()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:
- Change the superclass to
RecyclerView.Adapterfor paging through views, orFragmentStateAdapterfor paging through fragments. - Change the constructor parameters in fragment-based adapter classes.
- Override
getItemCount()instead ofgetCount(). - Override
createFragment()instead ofgetItem()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
- ViewPager2 samples on GitHub
Videos
- Turning the Page: Migrating to ViewPager2 (Android Dev Summit '19)