事件分發Demo-中插卡片

要求:中插卡片不支持ViewPager左右滑動,支持Feed流豎向滑動,支持內部卡片橫向滑動。UI效果圖如下:

設計思路:如果子視圖消費事件,並且沒有調用disallowIntercept方法,理論上最外層視圖會優先攔截事件。

由於這個需求ViewPager和RecyclerView攔截事件的條件有重合,而且我們想讓RecyclerView優先攔截事件,我們只能定製ViewPager、RecyclerView,並通過中插卡片Layout來控制ViewPager和RecyclerView的攔截標記位。

先打開RecyclerView攔截標記位,然後在RecyclerView的onInterceptTouchEvent中調用disallow方法,並且在中插卡片Layout的dispatch方法中監聽ACTION_UP和ACTION_CANCEL事件,恢復ViewPager和RecyclerView可攔截狀態

需要注意的是,中插卡片Layout一定要消費事件(onTouchEvent返回true),否則沒辦法主動控制點擊到卡片灰色部分的邏輯。

關鍵代碼如下:

class ForbidHorizontalScrollLinearLayout : LinearLayout {

    private var mTouchSlop: Int = 0
    private var mInitialMotionX: Float = 0f
    private var mInitialMotionY: Float = 0f

    constructor(context: Context?) : super(context) {
        init(context)
    }

    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
        init(context)
    }

    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        init(context)
    }

    fun init(context: Context?) {
        val configuration: ViewConfiguration? = context?.let { ViewConfiguration.get(it) }
        mTouchSlop = configuration?.scaledTouchSlop ?: 8 * 2
    }

    /**
     * RecyclerView
     *
     * final ViewConfiguration vc = ViewConfiguration.get(context);
     * mTouchSlop = vc.getScaledTouchSlop();
     * if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
     *    mLastTouchX = x;
     *    startScroll = true;
     * }
     * if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
     *    mLastTouchY = y;
     *    startScroll = true;
     * }
     *
     * ViewPager
     *
     * final ViewConfiguration configuration = ViewConfiguration.get(context);
     * mTouchSlop = configuration.getScaledPagingTouchSlop();
     * if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {
     *     mIsBeingDragged = true;
     * }
     *
     */
    override fun dispatchTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                mInitialMotionX = event.x
                mInitialMotionY = event.y
                changeAllScrollViewState(false)
            }
            MotionEvent.ACTION_MOVE -> {
                handleRecyclerView(event)
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                changeAllScrollViewState(true)
            }
        }
        return super.dispatchTouchEvent(event)
    }

    private fun changeAllScrollViewState(canScroll: Boolean) {
        var viewParent = parent
        while (viewParent != null) {
            if (viewParent is ScrollStateConfigurableViewPager) {
                viewParent.scrollEnable = canScroll
            } else if (viewParent is ScrollStateConfigurableRecyclerView) {
                viewParent.scrollEnable = canScroll
            }
            viewParent = viewParent.parent
        }
    }

    private fun switchOnRecyclerView() {
        var viewParent = parent
        while (viewParent != null) {
            if (viewParent is ScrollStateConfigurableRecyclerView) {
                viewParent.scrollEnable = true
                break
            }
            viewParent = viewParent.parent
        }
    }

    private fun handleRecyclerView(event: MotionEvent) {
        if (abs(event.y - mInitialMotionY) > mTouchSlop) {
            if (abs(event.x - mInitialMotionX) * 0.5f < abs(event.y - mInitialMotionY)) {
                switchOnRecyclerView()
            }
        }
    }

    /**
     * 強制返回true
     */
    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean = true
}

RecyclerView:

class ConfigurableRecyclerView : PeppaPagingRecyclerView {

    var scrollEnable = true

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)

    constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle)

    override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
        return if (scrollEnable) {
            if (super.onInterceptTouchEvent(e)) {
                requestDisallowInterceptTouchEvent(true)
                true
            } else {
                false
            }
        } else {
            false
        }
    }
}

ViewPager:

class ConfigurableViewPager : SSViewPager {

    var scrollEnable = true

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)

    override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
        return if (scrollEnable) {
            return super.onInterceptTouchEvent(event)
        } else {
            false
        }
    }
}

 

發佈了38 篇原創文章 · 獲贊 7 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章