For those of you are interested in knowing that how can you lazily inject your fragments when working with Android ViewPager2 and attach TabLayout with TabLayoutMediator then there will be vital piece of information for you in this article. Maybe some of you guys are already working with Android ViewPager2 to transform your android project. But as they say, always listen to other people’s opinion and in my case read other person opinion 😂. Without further ado grab your snacks and sit tight with me on this to glance at the style of

ViewPager2.

Flashback of Android ViewPager2

Google announced the

ViewPager2 in Google IO/19. It can be seen from the name that it is an upgraded version of ViewPager. The biggest change in the ViewPager2 API is that it now uses the RecyclerView. In this way, we can use the RecyclerView.Adapter with VP2 in our adapter class.

Android ViewPager2 vs ViewPager

Here are the changes occurred in ViewPager2 compared to ViewPager.

ViewPager2 ViewPager
Inherit from ViewGroup
Declared as final. We can no longer modify the code.
Inherit from ViewGroup
Not final. Inherit it and moodify the code.
Use FragmentStateAdapter Use FragmentStatePagerAdater
In order to get notified about page change, we need to use the `registerOnPageChangeCallback`. This method accepts the ViewPager2.OnPageChangeCallback an abstract class. In order to get notified about page change, we need to use the `addOnPageChangeListener`. This method accepts the ViewPager.OnPageChangeListener an abstract class.
1600 lines of code. 3000 lines of code.
Removed pageMargin method Still have the pageMargin method

The new features and APIs listed above may not be complete. If there are any omissions, you can leave a message below in the comments section.

Whats new in ViewPager2

As I mentioned above, the most important point is that ViewPager2 is built on RecyclerView component. Therefore it has access to the benefits of DiffUtill too.

  1. Allows vertical paging with LayoutManager support.
  2. Programmatic scrolling is spoorted. Simulate the user sliding the page through the fakeDragBy(offsetPx).
  3. Easy transformation with CompositePageTransformer.
  4. Improved dataset change notifications.
  5. ItemDecorator introduced with behavior consistent with RecyclerView.
  6. Support DiffUtil, you can add item animation of data collection changes.

Implementation Of ViewPager2

It is located in the viewpager2 package, not built into system source code like ViewPager. You may have visibility of the ViewPager2 classes in your project without adding an explicit dependency on the androidx.viewpager2:viewpager library, it means you have a transitive dependency on the androidx.viewpager2:viewpager library. It will be the case that one of the libraries which you have an explicit dependency on the androidx.viewpager2:viewpager library. The com.google.android.material:material dependency also have added the ViewPager2 dependency in it.

Therefore using it requires additional dependency. Add the following dependency into app-level build.gradle file or directly add the dependency.

dependencies {
     implementation 'com.google.android.material:material:1.6.1'
}

After sync the Gradle, we can use the ViewPager2. Define the layout file like this.

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

Just to show a simple sample I have write an xml item_page file for single item.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">

    <ImageView
        android:id="@+id/item_image_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

Writing Adapter For ViewPager2

Because ViewPager2 encapsulates RecyclerView inside, so the adapter will be same as like RecyclerView.Adapter.

class MyAdapter contructor(private val itemResources: List<Int>) : RecyclerView.Adapter<MyAdapter.PagerViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PagerViewHolder {
        return PagerViewHolder.create(parent)
    }

    override fun onBindViewHolder(holder: PagerViewHolder, position: Int) {
        val item = itemResources[position]
        holder.bind(item)
    }

    override fun getItemCount(): Int {
        return itemResources.size
    }
}

class PagerViewHolder private constructor(view: View) : RecyclerView.ViewHolder(view) {
       
        companion object {
        fun create(parent: ViewGroup): PagerViewHolder {
            return PagerViewHolder(
                LayoutInflater.from(parent.context).inflate(R.layout item_page., parent, false)
            )
        }
    }
        fun bindData(itemResource: Int) {
            itemView.findViewById<ImageView>(R.id.item_image_view).setImResource(itemResource)
        }
    }

I have demonstrated a simple ViewHolder and adapter which just shows ImageView in the list item but you can add more elements depending on your needs.

Set Adapter for ViewPager2 in Activity or Fragment

All of the basic step are done. Now we just need to connect the dots with each other.

val items = listOf(   // 1
    R.drawable.image_1,
    R.drawable.image_2,
    R.drawable.image_3,
    R.drawable.image_4
)
val viewPager2  = view.findViewById<ViewPager2>(R.id.viewPager2) // 2
val adapter = MyAdapter(items)  // 3
viewPager2.adapter = adapter  // 4

Here we:

  1. Initialize the dummy list of items so that we can see the result of swiping.
  2. Get the reference of ViewPager2 from xml file.
  3. Create new instance of MyAdapter which accepts the List<Int> as constructor parameter.
  4. Finally, set the adapter reference to viewPager instance.

Fun part where all the hard work pays. Seeing the Result 🥳:

Sliding Android ViewPager2 Vertically

It comes with an option to switch orientation easily, which was tough to do using the original ViewPager. This is now easy because ViewPager2 uses a RecyclerView which in turn uses a LayoutManager to manage the positioning of views inside it.

viewPager2.orientation = ViewPager2.ORIENTATION_VERTICAL

Take a look at the effect:

So far it is looking same as when we implement the RecyclerView.

Apply RTL Support for the Android ViewPager2

RTL stands for right-to-left. In languages like Arabic, where words go from right to left, it’s essential you take measures to ensure your app doesn’t break. Supporting RTL in ViewPager isn’t easy.

However, ViewPager2 comes with RTL support. To try it, aopy the following code:

viewPager2.layoutDirection = ViewPager2.LAYOUT_DIRECTION_RTL

The code above forces the layout direction of both widgets to RTL.

Android ViewPager2 With Fragments

Early in the article, I’ve mentioned that FragmentStateAdapter in ViewPager2 replaces FragmentStatePagerAdater of ViewPager.

private val fragments = listOf(  // 1
   FirstFragment(),
   SecondFragment(),
   ThirdFragment(),
   FourthFragment()
)

internal inner class ViewPager2StateAdapter : FragmentStateAdapter(activity) { // 2

        override fun getItemCount() = fragments.size  // 3

        override fun createFragment(position: Int): Fragment {
            return fragments[position] // 4
        }
    }

Here’s a breakdown of the code we’ve added:

  1. Collections of fragments to show inside the android viewpager.
  2. The FragmenStateAdapter takes three types of parameter Fragment, FragmentActivity, or FragmentManager with Lifecycle. Currently we’re passing activity reference.
  3. Returns the total number of items in the data set held by adapter.
  4. Returns the instance of fragment with-respect to viewpager position.

FYI: These fragments only created once at the start and we simply returns the instance of it. But lets take a scenario where user opens the app and all fragments instance created at start. Benefit of it that we only create fragments instances at once instead of everytime user navigate to that page. But what if we want to create instance of fragment at once when a user navigate to that page?

Here comes the lazily injected fragments.

Android ViewPager2 With Lazy Fragments Instance Creation

This comes in very handy and save a lot of memory consumption at the app start, when ViewPager2 holder activity or fragment initialze it. We create instance of fragment only when user navigate to that specific fragment.

private val fragments = listOf(
   lazy { FirstFragment() },  // 1
   lazy { SecondFragment() } ,
   lazy { ThirdFragment() },
   lazy { FourthFragment()} 
)

internal inner class ViewPager2StateAdapter : FragmentStateAdapter(activity) {

        override fun getItemCount() = fragments.size 

        override fun createFragment(position: Int): Fragment {
            return fragments[position].value  // 2
        }
    }

Taking each line one-by-one:

  1. Simply wrapping our instance of fragments into lazy {}. You can read more about the benefits of lazy block here.
  2. Returns the value of each fragment.

Listen for Page Sliding Events in Android ViewPager2

As mentioned above, we need to override three methods to set the listener event of page sliding for ViewPager and now with android ViewPager2 we only need to override the required method to set the listener event because OnPageChangeCallback is an abstract class.

val viewPagerChangeCallback = object : ViewPager2.OnPageChangeCallback() {
  override fun onPageSelected(position: Int) {
      // perfom action regarding position
  }
}
viewPager2.registerOnPageChangeCallback(viewPagerChangeCallback)

This code above uses Kotlin’s object expression to anonymously implement ViewPager2.OnPageChangeCallback and overrides only onPageSelected(position: Int). The ViewPager2.OnPageChangeCallback has three abstract methods. You can check them out here.

If you need to unregister the callback when you no longer want to listen to the changes. In onDestroy(), add the following code below:

viewPager2.unregisterOnPageChangeCallback(viewPagerChangeCallback)

Disable User Interaction With ViewPager2

We know that when using ViewPager and want to prohibit the user from sliding, we simply need to rewrite ViewPager’s onInterceptTouchEvent method. But as we know ViewPager2 is declared as final, we can no longer inherit with it. So, how should we prohibit sliding of android ViewPager2? In fact, this function has been provided for us with just one parameter.

viewPager2.isUserInputEnabled = false

Fake Drag of ViewPager2

Androidx ViewPager2 adds a fakeDragBy method. This method can simulate drag and drop. Lets see the implementation of it.

viewPager2.beginFakeDrag() // 1
if (viewPager2.fakeDragBy(-310f))  // 2
    viewPager2.endFakeDrag()  // 3

In the above code we:

  1. Start a fake drag to simulate drag and drop.
  2. Fake drag by an offset in pixels.
  3. End a fake drag for the pager.

Connect TabLayout (TabLayoutMediator) With Android ViewPager2

Showing tabs whenever you use swipeable screens is a good way to let the users know there’s more to see by swiping. The process of adding tabs has changed a bit ViewPager2.

So how TabLayout can be used for ViewPager2? This requires us to know a new class, TabLayoutMediator which is new class in material dependency.

Open the xml realted file and paste the following code:

<LinearlLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

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

      <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/viewPager2"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

</LinearlLayout> 

Now here comes the tricky part. To link the TabLayout and ViewPager2, we have to use TabLayoutMediator. This synchronizes the ViewPager2 and TabLayout to change the position when one gets clicked or swiped.

Open the activity or fragment file linked with the specific xml and add the following code.

val tabItemsName = listOf("Tab1", "Tab2", "Tab3")

val viewPager2 = view.findViewById<ViewPager2>(R.id.viewPager2)
val tabLayout = view.findViewById<TabLayout>(R.id.tabLayout)
TabLayoutMediator(tabLayout, viewPager2) {tab, position -> 
   tab.text = tabItemsName[position]
}.attach()

The TabLayoutMediator class accepts three parameters. These are TabLayout, a ViewPager2, and last parameter of function is function or, in this case, a functional interface, and we’re passing a lambda expression, we can specify it outside the paranthese.

The important thing to notice is the attach() method. This method links the functionalities of ViewPager2and TabLayout together.

Summary

To sum up, we have learned about new features of android ViewPager2 and its usages with Fragment and TabLayout. In general ViewPager2 has great improvement in performance and function compared to ViewPager. You can use all the advantages of ViewPager2 in your projects as well and hopefully, it will give you peace of mind and you will be satisfied with the result.

If I did miss something in the article, please let me know in the comments section.

Thank you for being here and keep reading…😇