ViewPager2 improves on the previous ViewPager implementation:

  • FragmentTransactionCallback interface, is used to monitor changes occurred within FragmentStateAdapter fragments of life cycle

  • RTL (right-to-left) layout support

  • Vertical support

  • Reliable Fragment support (including handling changes to the underlying Fragment collection)

  • Animation of data set changes (including DiffUtil support)

  • ItemDecorator, which behaves the same as RecyclerView.

  • MarginPageTransformer, to provide the ability to create Spaces between pages (outside of page margins).

  • CompositePageTransformer to provide the ability to compose multiple PageTransformers

ViewPager2 samples github:

Github.com/android/vie…

In addition to horizontal paging, vertical paging is also supported, thanks to the recyclerView function of ViewPage2, which dynamically changes the Fragment collection and calls notifyDatasetChanged() to update the interface.

ViewPager, paging through a fixed number of fragments, use FragmentPagerAdapter, paging through a large number of fragments, use FragmentStateAdapter and ViewPager2 has only a FragmentStateAdapter

The getPageWidth() method for ViewPager2 is not supported. If you currently use getPageWidth() for ViewPager to quickly view adjacent pages, you must use the clipToPadding property of RecyclerView instead

Used with TAB:

 <! -- 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>
Copy the code

TabLayoutMediator combines the two, similar to setupWithViewPager

 val tabLayout = view.findViewById(R.id.tab_layout)
            TabLayoutMediator(tabLayout, viewPager) { tab, position ->
                tab.text = "OBJECT ${(position + 1)}"
            }.attach()
Copy the code

In order to support the same ViewPager2 object within the scroll view direction, if you want to scroll nested elements, you must to ViewPager2 objects call requestDisallowInterceptTouchEvent (). The ViewPager2 nested scroll example shows an approach to this problem using a generic custom wrapper layout.

Github.com/android/vie…

/** * Layout to wrap a scrollable component inside a ViewPager2. Provided as a solution to the problem * where pages of ViewPager2 have nested scrollable elements that scroll in the same direction as * ViewPager2. The scrollable element needs to be the immediate and only child of this host layout. * * This solution has limitations when using multiple levels of nested scrollable elements * (e.g. a horizontal RecyclerView in a vertical RecyclerView in a horizontal ViewPager2). */
class NestedScrollableHost : FrameLayout {
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)

    private var touchSlop = 0
    private var initialX = 0f
    private var initialY = 0f
    private val parentViewPager: ViewPager2?
        get() {
            var v: View? = parent as? View
            while(v ! =null && v !is ViewPager2) {
                v = v.parent as? View
            }
            return v as? ViewPager2
        }

    private val child: View? get() = if (childCount > 0) getChildAt(0) else null

    init {
        touchSlop = ViewConfiguration.get(context).scaledTouchSlop
    }

    private fun canChildScroll(orientation: Int, delta: Float): Boolean {
        val direction = -delta.sign.toInt()
        return when (orientation) {
            0-> child? .canScrollHorizontally(direction) ? :false
            1-> child? .canScrollVertically(direction) ? :false
            else -> throw IllegalArgumentException()
        }
    }

    override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
        handleInterceptTouchEvent(e)
        return super.onInterceptTouchEvent(e)
    }

    private fun handleInterceptTouchEvent(e: MotionEvent) {
        valorientation = parentViewPager? .orientation ? :return

        // Early return if child can't scroll in same direction as parent
        if(! canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) {
            return
        }

        if (e.action == MotionEvent.ACTION_DOWN) {
            initialX = e.x
            initialY = e.y
            parent.requestDisallowInterceptTouchEvent(true)}else if (e.action == MotionEvent.ACTION_MOVE) {
            val dx = e.x - initialX
            val dy = e.y - initialY
            val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL

            // assuming ViewPager2 touch-slop is 2x touch-slop of child
            val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f else 1f
            val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f

            if (scaledDx > touchSlop || scaledDy > touchSlop) {
                if (isVpHorizontal == (scaledDy > scaledDx)) {
                    // Gesture is perpendicular, allow all parents to intercept
                    parent.requestDisallowInterceptTouchEvent(false)}else {
                    // Gesture is parallel, query child if movement in that direction is possible
                    if (canChildScroll(orientation, if (isVpHorizontal) dx else dy)) {
                        // Child can scroll, disallow all parents to intercept
                        parent.requestDisallowInterceptTouchEvent(true)}else {
                        // Child cannot scroll, allow all parents to intercept
                        parent.requestDisallowInterceptTouchEvent(false)}}}}}}Copy the code

PageTransformer page transition animation

val rotateChecked = false
        val translateCheckBoxChecked = true
        val scaleCheckBoxChecked = true
        val translateX = viewPager.orientation == ORIENTATION_VERTICAL &&
                translateCheckBoxChecked
        val translateY = viewPager.orientation == ORIENTATION_HORIZONTAL &&
                translateCheckBoxChecked

        val mAnimator = ViewPager2.PageTransformer { page, position ->
            val absPos = Math.abs(position)
            page.apply {
                rotation = if (rotateChecked) position * 360 else 0f
                translationY = if (translateY) absPos * 500f else 0f
                translationX = if (translateX) absPos * 350f else 0f
                if (scaleCheckBoxChecked) {
                    val scale = if (absPos > 1) 0F else 1 - absPos
                    scaleX = scale
                    scaleY = scale
                } else {
                    scaleX = 1f
                    scaleY = 1f}}}// Combine effect
        viewPager.setPageTransformer(CompositePageTransformer().also {
            it.addTransformer(mAnimator)
            it.addTransformer(MarginPageTransformer(50))})Copy the code