Pagination
The ViewPager2 widget allows you to navigate between sibling screens, such as tabs, with a horizontal finger gesture, or swipe. This navigation pattern is also referred to as horizontal paging.
ViewPager2 is built on RecyclerView, therefore you can set vertical orientation and use DiffUtil class with it.
ViewPager2 mimics ViewPager. It is recommended to use ViewPager2 rather ViewPager.
Add dependency
dependencies {
implementation("androidx.viewpager2:viewpager2:1.0.0")
}
TabLayout widjet is used to display page titles.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- If you have a large or potentially infinite number of pages, set the android:tabMode attribute on your TabLayout to "scrollable".
-->
<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>
A TabLayoutMediator class is used to link the TabLayout to the ViewPager2
val tabLayout = view.findViewById(R.id.tab_layout)
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = titles[position]
}.attach()
FragmentStateAdapter may be used as adapter to provide fragments for pages. You can specify host fragment or host activity.
class PagesAdapter(hostFragment: Fragment) : FragmentStateAdapter(fragment) {
override fun getItemCount(): Int = 100
override fun createFragment(position: Int): Fragment {
// Return a NEW fragment instance in createFragment(int)
val fragment = DemoObjectFragment()
fragment.arguments = Bundle().apply {
// Our object is just an integer :-P
putInt(ARG_OBJECT, position + 1)
}
return fragment
}
}
class PagesAdapter(activity: MainScreenActivity) : FragmentStateAdapter(activity) {
override fun getItemCount(): Int {
TODO("Not yet implemented")
}
override fun createFragment(position: Int): Fragment {
TODO("Not yet implemented")
}
}
Set adapter to your viewPager2.
val viewPager: ViewPager2 by lazy { findViewById(R.id.view_pager2) }
var adapter: PagesAdapter? = null
// ...
override fun onCreate(savedInstanceState: Bundle?) {
// ...
adapter = PagesAdapter(this)
viewPager.adapter = adapter
}
If pages represent by views use RecyclerView.Adapter as usual. Actually FragmentStateAdapter also extends RecyclerView.Adapter.
public abstract class FragmentStateAdapter extends
RecyclerView.Adapter<FragmentViewHolder> implements StatefulAdapter {
// ...
}
ViewPager2.OnPageChangeCallback is callback class. Most useful method is onPageSelected()
viewPager2.registerOnPageChangeCallback(object : OnPageChangeCallback() {
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
// do something
}
override fun onPageSelected(position: Int) {
// do something
}
})
A PageTransformer is invoked whenever a visible/attached page is scrolled. This offers an opportunity for the application to apply a custom transformation to the page views using animation properties.
val viewPager: ViewPager2 = findViewById(R.id.pager)
...
viewPager.setPageTransformer(ZoomOutPageTransformer())
You can find more examples and libraries on Github.
RecyclerView
You can implement pagination using RecyclerView.
PagerSnapHelper class can help achieve a similar behavior to ViewPager within RecyclerView. It supports pager style snapping in either vertical or horizontal orientation.
Additionallu you must set both RecyclerView and the items of the RecyclerView.Adapter to have ViewGroup.LayoutParams.MATCH_PARENT height and width.
myRecyclerView.setLayoutManager(
LinearLayoutManager(context,LinearLayoutManager.HORIZONTAL, false)
)
val snapHelper: SnapHelper = PagerSnapHelper()
snapHelper.attachToRecyclerView(myRecyclerView)
Similarly LinearSnapHelper class implements snapping to the center of the target child view to the center of the attached RecyclerView. If you intend to change this behavior then override SnapHelper.calculateDistanceToFinalSnap().
ViewPager
ViewPager is a layout manager that allows the user to flip left and right through pages of data. Each page can be View or Fragment.
Although ViewPager is legacy, there are a lot codes, that use it.
There are standard adapters for using with the ViewPager:
- FragmentPagerAdapter - implementation of PagerAdapter that represents each page as a Fragment. All pages that is persistently kept in memory. It detaches the views from fragments which are currently not visible. Therefore you must make sure to clear any references to the current View or Context in onDestroyView(), otherwise you will get memory leak. Good for a fixed number of pages.
- FragmentStatePagerAdapter - allows keep in memory only visible pages. It completely removes fragments from the FragmentManager once they are out of reach. The state of the removed Fragments is stored inside the FragmentStatePagerAdapter. Good for lists with an unknown count or for lists where the pages change a lot.
TabLayout from material design is recommended way to display page titles. You can place ViewPager within CoordinatorLayout if necessary.
Use setupWithViewPager() to link the TabLayout to the ViewPager
val tabLayout = view.findViewById(R.id.tab_layout)
tabLayout.setupWithViewPager(viewPager)
PagerTitleStrip and PagerTabStrip classes are legacy.
PagerTitleStrip is a non-interactive indicator of the current, next, and previous pages of a ViewPager. It is intended to be used as a child view of a ViewPager widget in your XML layout.
PagerTabStrip is an interactive indicator of the current, next, and previous pages of a ViewPager. In other words, user can click on the tabs to switch between pages. It is intended to be used as a child view of a ViewPager widget in your XML layout.
ViewPager.OnPageChangeListener is a callback interface for responding to changing state of the selected page. The most useful method is onPageSelected().
Note 1. Calling notifyDataSetChanged() method will recreate all pages even if not necessary. At this moment user may see blink.
Note 2. Pages don't have id, only positions. When FragmentStatePagerAdapter delete fragment, it save fragment's bundle. In case one fragment is replaced by another after calling notifyDataSetChanged(), the adapter assigns the cached bundle of the old fragment to the new fragment, which is not right.