美文网首页
ViewPager2滑动冲突

ViewPager2滑动冲突

作者: 霁逸lei | 来源:发表于2022-06-28 17:31 被阅读0次

    ViewPager2 嵌套 增加中间层处理事件拦截

    ViewPager2 嵌套 ViewPager2

    package com.tdx.tdxtappview
    
    import android.content.Context
    import android.util.AttributeSet
    import android.view.MotionEvent
    import android.widget.RelativeLayout
    import androidx.viewpager2.widget.ViewPager2
    import kotlin.math.abs
    
    /**
     * ViewPager2 嵌套 ViewPager2 滑动冲突处理
     */
    class ViewPager2Container @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : RelativeLayout(context, attrs, defStyleAttr) {
    
        private var mViewPager2: ViewPager2? = null
        private var disallowParentInterceptDownEvent = true
        private var startX = 0
        private var startY = 0
    
        override fun onFinishInflate() {
            super.onFinishInflate()
            for (i in 0 until childCount) {
                val childView = getChildAt(i)
                if (childView is ViewPager2) {
                    mViewPager2 = childView
                    break
                }
            }
            if (mViewPager2 == null) {
                throw IllegalStateException("The root child of ViewPager2Container must contains a ViewPager2")
            }
        }
    
        override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
            val doNotNeedIntercept = (!mViewPager2!!.isUserInputEnabled
                    || (mViewPager2?.adapter != null
                    && mViewPager2?.adapter!!.itemCount <= 1))
            if (doNotNeedIntercept) {
                return super.onInterceptTouchEvent(ev)
            }
            when (ev.action) {
                MotionEvent.ACTION_DOWN -> {
                    startX = ev.x.toInt()
                    startY = ev.y.toInt()
                    parent.requestDisallowInterceptTouchEvent(!disallowParentInterceptDownEvent)
                }
                MotionEvent.ACTION_MOVE -> {
                    val endX = ev.x.toInt()
                    val endY = ev.y.toInt()
                    val disX = abs(endX - startX)
                    val disY = abs(endY - startY)
                    if (mViewPager2!!.orientation == ViewPager2.ORIENTATION_VERTICAL) {
                        onVerticalActionMove(endY, disX, disY)
                    } else if (mViewPager2!!.orientation == ViewPager2.ORIENTATION_HORIZONTAL) {
                        onHorizontalActionMove(endX, disX, disY)
                    }
                }
                MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> parent.requestDisallowInterceptTouchEvent(false)
            }
            return super.onInterceptTouchEvent(ev)
        }
    
        private fun onHorizontalActionMove(endX: Int, disX: Int, disY: Int) {
            if (mViewPager2?.adapter == null) {
                return
            }
            if (disX > disY) {
                val currentItem = mViewPager2?.currentItem
                val itemCount = mViewPager2?.adapter!!.itemCount
                if (currentItem == 0 && endX - startX > 0) {
                    parent.requestDisallowInterceptTouchEvent(false)
                } else {
                    parent.requestDisallowInterceptTouchEvent(currentItem != itemCount - 1
                            || endX - startX >= 0)
                }
            } else if (disY > disX) {
                parent.requestDisallowInterceptTouchEvent(false)
            }
        }
    
        private fun onVerticalActionMove(endY: Int, disX: Int, disY: Int) {
            if (mViewPager2?.adapter == null) {
                return
            }
            val currentItem = mViewPager2?.currentItem
            val itemCount = mViewPager2?.adapter!!.itemCount
            if (disY > disX) {
                if (currentItem == 0 && endY - startY > 0) {
                    parent.requestDisallowInterceptTouchEvent(false)
                } else {
                    parent.requestDisallowInterceptTouchEvent(currentItem != itemCount - 1
                            || endY - startY >= 0)
                }
            } else if (disX > disY) {
                parent.requestDisallowInterceptTouchEvent(false)
            }
        }
    
        /**
         * 设置是否允许在当前View的{@link MotionEvent#ACTION_DOWN}事件中禁止父View对事件的拦截,该方法
         * 用于解决CoordinatorLayout+CollapsingToolbarLayout在嵌套ViewPager2Container时引起的滑动冲突问题。
         *
         * 设置是否允许在ViewPager2Container的{@link MotionEvent#ACTION_DOWN}事件中禁止父View对事件的拦截,该方法
         * 用于解决CoordinatorLayout+CollapsingToolbarLayout在嵌套ViewPager2Container时引起的滑动冲突问题。
         *
         * @param disallowParentInterceptDownEvent 是否允许ViewPager2Container在{@link MotionEvent#ACTION_DOWN}事件中禁止父View拦截事件,默认值为false
         *                          true 不允许ViewPager2Container在{@link MotionEvent#ACTION_DOWN}时间中禁止父View的时间拦截,
         *                          设置disallowIntercept为true可以解决CoordinatorLayout+CollapsingToolbarLayout的滑动冲突
         *                          false 允许ViewPager2Container在{@link MotionEvent#ACTION_DOWN}时间中禁止父View的时间拦截,
         */
        fun disallowParentInterceptDownEvent(disallowParentInterceptDownEvent: Boolean) {
            this.disallowParentInterceptDownEvent = disallowParentInterceptDownEvent
        }
    }
    
    

    ViewPager2 嵌ViewPager

    IllegalStateException: ViewPager2 does not support direct child views
    viewPager.setAdapter(new FragmentPagerAdapter(getChildFragmentManager(),BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
    
    package com.tdx.View
    
    import android.content.Context
    import android.util.AttributeSet
    import android.util.Log
    import android.view.GestureDetector
    import android.view.MotionEvent
    import android.view.View
    import android.view.ViewConfiguration
    import android.widget.FrameLayout
    import androidx.viewpager.widget.ViewPager
    import androidx.viewpager2.widget.ViewPager2
    import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
    import kotlin.math.absoluteValue
    import kotlin.math.sign
    
    /**
     * 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 {
            //处理子View 为ViewPager
            if (child is ViewPager && e.action == MotionEvent.ACTION_MOVE){
                var pager: ViewPager = child as ViewPager
                val currentItem = pager?.currentItem
                val itemCount = pager?.adapter!!.count
                //最左侧左划,最右侧右滑 拦截不下发事件到ViewPager
                var startScrollLeft = (currentItem == 0 && e.x - initialX > 0)
                var endScrollRight = (currentItem == itemCount - 1 && e.x - initialX <= 0)
                val dx = e.x - initialX
                val dy = e.y - initialY
                if (dx.absoluteValue > dy.absoluteValue && (startScrollLeft || endScrollRight)){
                    parent.requestDisallowInterceptTouchEvent(false)
                    return true
                }
            }
            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)
                        }
                    }
                }
            } else {
                parent.requestDisallowInterceptTouchEvent(false)
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:ViewPager2滑动冲突

          本文链接:https://www.haomeiwen.com/subject/oahjurtx.html