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 ViewPager2Google 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.
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.
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.
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>
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.
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:
items
so that we can see the result of swiping.ViewPager2
from xml file.MyAdapter
which accepts the List<Int>
as constructor parameter.viewPager
instance.Fun part where all the hard work pays. Seeing the Result 🥳:
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.
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.
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:
fragments
to show inside the android viewpager
.viewpager
position.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.
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:
lazy {}
. You can read more about the benefits of lazy block here.value
of each fragment.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)
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
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:
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 ViewPager2
and TabLayout together.
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…😇
Quick Links
Legal Stuff
Social Media