viewPager2 互相嵌套

1 阅读1分钟

官方推荐的 具体的继承 Layout 可以根据实际的需求进行修改

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)
          }
        }
      }
    }
  }
}

使用方法

<com.app.main.NestedScrollableHost
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

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

</com.linxuan.android.home.NestedScrollableHost>