Search code examples
androidandroid-viewpager

viewpager is not working when using in viewpager


I am making a shopping mall app now. The root uses viewpager to switch the fragment screen. The banner viewpager is implemented by adding a viewpager to each fragment. So the banner viewpager doesn't accept any events. When I swipe the banner viewpager, the root viewpager swipe and the screen moves. I think root viewpager is blocking sub viewpager. How can I solve this? I can't override canSroll because I use viewpager2.

enter image description here


Solution

  • ViewPager2 does not natively support nested scroll views in cases where the scroll view has the same orientation as the ViewPager2 object that contains it.

    Wrap the child ViewPager2 with this class in xml layout.

    Try this :

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