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
- ViewPager2 samples on GitHub
Videos
- Turning the Page: Migrating to ViewPager2 (Android Dev Summit '19)