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
ViewPager
usedPagerAdapter
to page through views, useRecyclerView.Adapter
withViewPager2
. - When
ViewPager
usedFragmentPagerAdapter
to page through a small, fixed number of fragments, useFragmentStateAdapter
withViewPager2
. - When
ViewPager
usedFragmentStatePagerAdapter
to page through a large or unknown number of fragment, useFragmentStateAdapter
withViewPager2
.
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 orFragment
object where theViewPager2
object resides. In most cases, this is the better option. - A
FragmentManager
object and aLifecycle
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()
, 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.Adapter
for paging through views, orFragmentStateAdapter
for 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
Videos
- Turning the Page: Migrating to ViewPager2 (Android Dev Summit '19)