Android ViewPager2 & TabLayout

Myrick Chow
ITNEXT
Published in
5 min readMay 15, 2019

--

ViewPager2 is introduced in this year 2019 Google I/O and is a replacement of the old ViewPager, which was created in 2011. It includes some new features to enhance UI and coding experience, including:

  1. Right-to-left layout support
  2. Vertical orientation (Scroll vertically)
  3. modifiable Fragment collections
Screenshot of ViewPager2 source code from Google

ViewPager2 is a ViewGroup backed by a RecyclerView and thus the handling method is similar to that for RecyclerView. ViewPager2 requires an adapter to show its contents and the adapter can be either RecyclerView adapter or FragmentStateAdapter.

This article will cover the basic handling of ViewPager2 and the linking with a TabLayout. If you have been familiar with the setup of ViewPager2, you could jump to “Trick” session directly.

Setup

ViewPager2 is packed inside the latest AndroidX library of JetPack instead of Material Component library. Thus, we have to import it separately with the following gradle code:

Bear in mind that AndroidX library should not be kept together support library to prevent any unexpected results.

XML layout

Simply add ViewPager2 widget to your layout:

Define a RecyclerView adapter and cell layout for ViewPager2

layout_demo_viewpager2_cell.xml

Bind RecyclerView adapter with ViewPager2

The method to set an adapter to ViewPager2 is

Simple result:

Trick — TabLayout cannot be bound with ViewPager2

Screenshot from Android Studio

In the current release of Material Component library (version: 1.1.0-alpha06), TabLayout widget is not yet ready to bind with a ViewPager2 widget natively. So, how do we work around with it? According to Nikola Despotoski’s answer in StackOverflow, we can manually use the class TabLayoutMediator to bind a TabLayout widget with a ViewPager2.

Screenshot from the source code of TabLayoutMediator

However, according to the source code, TabLayoutMediator is restricted to library level and cannot be called directly from developer’s code. Therefore, we have to make a copy of this file to our project and call it locally. According to the comments in source code, attach() can only be called after TabLayoutMediator is initialised.

Screenshot of TabLayoutMediator source code

The TabLayoutMediator.OnConfigureTabCallback is a user friendly function which is called when eachTabLayout.Tab is initialised or data are changed. It exposes the opportunity to style all the tabs.

Screenshot of TabLayoutMediator source code

Final result:

RecyclerView adapter Vs FragmentStateAdapter

Screenshot from RecyclerView.Adapter Google documentation
Screenshot from FragmentStateAdapter Google documentation

As mentioned at the start of the article, ViewPager2 accepts both RecyclerView.Adapter and FragmentStateAdapter.

FragmentStateAdapter is a direct child of RecyclerView.Adapter. The main difference between them is that RecyclerView.Adapter inflates View but FragmentStateAdapter inflates Fragment.

Thus, RecyclerView.Adapter can be used when each page in ViewPager2 is made for displaying static information only, andFragmentStateAdapter can be used when lifecycle of each page has to be taken into consideration.

For example, photo viewer ViewPager can use RecyclerView.Adapter to display a list of images, and application form pages with EditText can use FragmentStateAdapter to use onSaveInstanceState() in each Fragment if necessary.

Integration with TabLayout

It was simple to integrate a TabLayout with the old version of ViewPager by simply add it as a child of ViewPager and position by XML property android:layout_gravity. See this Google documentation.

<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top" />
</android.support.v4.view.ViewPager>

However, ViewPager2 (version 1.0.0-alpha04) does not support direct child views which means that TabLayout cannot be added to ViewPager2 as a ViewPager.DecorView. TabLayout has to be placed inside a FrameLayout together with ViewPager2 in order to achieve the same effect.

Screenshot of ViewPager2 about limitation of child view

As the comment written in the above function, Google probably would include back the feature of supporting direct child views in the next or stable version of ViewPager2 in the future.

Conclusion:

  1. ViewPager2 includes RecycleView to original ViewPager and makes the coding much easier than before.
  2. ViewPager2 (version 1.0.0-alpha04) is not available to be bound by a TabLayout natively . TabLayoutMediator is a good work around for this issue.
  3. ViewPager2 requires an adapter to control the content in each page. It can be either RecyclerView.Adapter or FragmentStateAdapter. RecyclerView.Adapter is suitable for static content case and FragmentStateAdapter is suitable for content which requires to listen to lifecycle event.
  4. ViewPager2 is not allowed to have any child views. TabLayout has to be placed inside another ViewGroup together with ViewPager2.

Further readings:

1. It is common to set ViewPager to have horizontal margin while keeping the previous and next pages visible to user. However, the function is missing in ViewPager2 and caching mechanism is disabled by default. Let’s see how we can fix this issue here.

2. Dot-styled TabItem is not officially documented. The process is concluded to only 4 simple steps.

3. TabLayout works closely with ViewPager and ViewPager2. However, styling TabLayout and TabItem is sometimes confusing. Read the following article if you need.

Please follow me at Twitter@myrick_chow for more information. Thank you for reading this article. Have a nice day! 😄

--

--

Mobile Lead @REAL Messenger Inc. https://real.co Focus on Android & iOS Native programming.