Android ViewPager2 笔记

Posted aikongmeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android ViewPager2 笔记相关的知识,希望对你有一定的参考价值。

ViewPager2 对之前的 ViewPager 实现的改进:

  • FragmentTransactionCallback 接口,用于监听 FragmentStateAdapter 内发生的 Fragment 生命周期变化

  • RTL(从右向左)布局支持

  • 垂直方向支持

  • 可靠的 Fragment 支持(包括处理底层 Fragment 集合的更改)

  • 数据集更改动画(包括 DiffUtil 支持)

  • ItemDecorator,其行为与 RecyclerView 一致。

  • MarginPageTransformer,以提供在页面之间(页面边衬区之外)创建空间的功能。

  • CompositePageTransformer,以提供组合多个 PageTransformer 的功能

ViewPager2 samples github:

https://github.com/android/views-widgets-samples/tree/master/ViewPager2

除了水平分页,还支持垂直分页, 这得益于viewpage2recyclerView的基础上, 可以动态的修改Fragment集合,调用notifyDatasetChanged()来通知更新界面.

ViewPager ,分页浏览固定数量的Fragment ,使用FragmentPagerAdapter, 分页较多时使用 FragmentStateAdapter
ViewPager2 只有 FragmentStateAdapter

不支持将 getPageWidth() 方法用于 ViewPager2。如果您目前将 getPageWidth() 用于 ViewPager 以便快速查看相邻页面,则必须改用 RecyclerViewclipToPadding 属性

与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>

TabLayoutMediator 将两则结合起来,类似于 ViewPager时的 setupWithViewPager

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

为了支持方向相同的 ViewPager2 对象内的滚动视图,如果您希望改为滚动嵌套的元素,则必须对 ViewPager2 对象调用 requestDisallowInterceptTouchEvent()。ViewPager2 嵌套滚动示例展示了一种使用通用自定义封装容器布局解决此问题的办法。

https://github.com/android/views-widgets-samples/blob/master/ViewPager2/app/src/main/java/androidx/viewpager2/integration/testapp/NestedScrollableHost.kt

/**
 * 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) {
        val orientation = 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)
                    }
                }
            }
        }
    }
}

PageTransformer 页面过渡动画

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
                }
            }
        }
		//组合效果
        viewPager.setPageTransformer(CompositePageTransformer().also {
            it.addTransformer(mAnimator)
            it.addTransformer(MarginPageTransformer(50))
        })

以上是关于Android ViewPager2 笔记的主要内容,如果未能解决你的问题,请参考以下文章

Android ViewPager2 笔记

Android ViewPager2 笔记

从 Viewpager2 片段访问父片段函数

ViewPager2 无法动态添加删除片段

ViewPager2:以编程方式选择特定片段

带有 Viewpager2 的关键 FragmentStateAdapter 的片段不再存在