滑动冲突1

1.先来看看原始需求,自定义一个seekbar

关键代码如下,

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                lastX = event.rawX.toInt()
                if (isStartAnimator) {
                    return false
                }
                // check finger is touch above slide
                if (event.x > slideLeft && event.x < slideBitmap.width + slideLeft) {
                    slideIsMoving = true
                    return true
                }
            }
            MotionEvent.ACTION_MOVE -> {
                val dx = event.rawX.toInt() - lastX
                slideLeft += dx
                if (slideLeft < 0) {
                    slideLeft = 0
                }
                if (slideLeft > slideBgWidth + margin * 2 - slideBitmap.width) {
                    slideLeft = slideBgWidth + margin * 2 - slideBitmap.width
                }
                if (slideIsMoving) {
                    invalidate()
                    lastX = event.rawX.toInt()
                }
            }
            MotionEvent.ACTION_UP -> if (slideIsMoving) {
                backZero()
                slideIsMoving = false
            }
            else -> {
            }
        }
        return super.onTouchEvent(event)
    }

主要流程就是监听用户触摸事件,当用户点击在滑块上面时,开始处理用户滑动事件(ACTION_DOWN 返回true),然后计算用户滑动距离,修改slideLeft 值,并且调用invalidate() 刷新界面。在onDraw中根据slideLeft值,修改滑块位置。

2.新的需求是在viewpager中,此时我的滑动就出现了问题

jumkq-xh0uv.gif

查看日志如下:

2021-10-15 15:21:56.324 17940-17940/com.example.myapplication I/CustomSeekBar: ACTION_DOWN
2021-10-15 15:21:56.433 17940-17940/com.example.myapplication I/CustomSeekBar: ACTION_MOVE
2021-10-15 15:21:56.450 17940-17940/com.example.myapplication I/CustomSeekBar: ACTION_MOVE
2021-10-15 15:21:56.466 17940-17940/com.example.myapplication I/CustomSeekBar: ACTION_CANCEL

正常情况下应该是down- move-up
此时出现了down-move-cancel
为什么会这样呢?

3.咱们带着问题来分析一下Android的事件分发处理流程

咱们从activity接收到事件开始,底层的不用在意。

先来看看activity布局层级

基于Android SDK 30普通布局


image.png

activity布局如下:


    

图中可以看到activity,和window其实都不是view,DecorView才是咱们布局的顶层view,然后到咱们自己的布局上一共已经有5层view。从第6层起,刚好就是咱们自己布局文件的根view。

seekBar onTouchEvent调用栈
再来看看activity事件分化流程
简易事件分发.png

以下是一次完整的事件分化处理过程:

请注意,viewPager onTouchEvent 默认返回true,以下log是我修改代码后的
2021-10-19 16:48:21.917 4857-4857/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 16:48:21.925 4857-4857/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 16:48:21.926 4857-4857/com.example.myapplication I/GUOHENG: CustomViewPager onInterceptTouchEvent
2021-10-19 16:48:21.931 4857-4857/com.example.myapplication I/GUOHENG: CustomSeekBar dispatchTouchEvent
2021-10-19 16:48:21.931 4857-4857/com.example.myapplication I/GUOHENG: CustomSeekBar onTouchEvent
2021-10-19 16:48:25.761 4857-4857/com.example.myapplication I/GUOHENG: CustomViewPager onTouchEvent
2021-10-19 16:48:28.814 4857-4857/com.example.myapplication I/GUOHENG: SeekBarActivity onTouchEvent

点击seekbar时的事件分化及处理

点击时
2021-10-19 17:00:41.669 5464-5464/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 17:00:41.669 5464-5464/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 17:00:41.669 5464-5464/com.example.myapplication I/GUOHENG: CustomViewPager onInterceptTouchEvent
2021-10-19 17:00:41.670 5464-5464/com.example.myapplication I/GUOHENG: CustomSeekBar dispatchTouchEvent
2021-10-19 17:00:41.670 5464-5464/com.example.myapplication I/GUOHENG: CustomSeekBar onTouchEventACTION_DOWN
2021-10-19 17:00:41.670 5464-5464/com.example.myapplication I/GUOHENG: CustomViewPager onTouchEvent
2021-10-19 17:00:41.780 5464-5464/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 17:00:41.780 5464-5464/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 17:00:41.780 5464-5464/com.example.myapplication I/GUOHENG: CustomViewPager onTouchEvent

滑动seekbar时的事件分化及处理

滑动时
2021-10-19 17:02:19.707 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 17:02:19.707 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 17:02:19.708 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager onInterceptTouchEvent
2021-10-19 17:02:19.708 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar dispatchTouchEvent
2021-10-19 17:02:19.708 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar onTouchEventACTION_DOWN
2021-10-19 17:02:19.778 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 17:02:19.778 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 17:02:19.778 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager onInterceptTouchEvent
2021-10-19 17:02:19.779 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar dispatchTouchEvent
2021-10-19 17:02:19.779 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar onTouchEventACTION_MOVE
2021-10-19 17:02:19.779 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity onTouchEvent
2021-10-19 17:02:19.794 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 17:02:19.794 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 17:02:19.794 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager onInterceptTouchEvent
2021-10-19 17:02:19.795 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar dispatchTouchEvent
2021-10-19 17:02:19.795 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar onTouchEventACTION_MOVE
2021-10-19 17:02:19.795 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity onTouchEvent
2021-10-19 17:02:19.812 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 17:02:19.813 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 17:02:19.813 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager onInterceptTouchEvent
2021-10-19 17:02:19.814 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar dispatchTouchEvent
2021-10-19 17:02:19.814 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar onTouchEventACTION_CANCEL
2021-10-19 17:02:19.816 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity onTouchEvent
2021-10-19 17:02:19.828 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 17:02:19.828 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 17:02:19.828 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager onTouchEvent

出现滑动冲突原因就是父view将事件抢过去处理了,给我们发了cancel事件。导致后续move事件被父view处理,seekbar接收不到后续事件。
通过视频也能看到滑动一段距离后,viewpager出现了滑动不了的提示。关键代码如下:


viewpager.java   onInterceptTouchEvent()
            case MotionEvent.ACTION_MOVE: {
               ......
                if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {
                     Log.v(TAG, "Starting drag!" + mTouchSlop);
                    mIsBeingDragged = true;
                    requestParentDisallowInterceptTouchEvent(true);
                    setScrollState(SCROLL_STATE_DRAGGING);
                    mLastMotionX = dx > 0
                            ? mInitialMotionX + mTouchSlop : mInitialMotionX - mTouchSlop;
                    mLastMotionY = y;
                    setScrollingCacheEnabled(true);
                } 

viewGroup.java
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }

解决冲突:在viewgroup中判断是否拦截当前事件前,会有一个是否允许拦截的判断,我们可以在滑块滑动时不让父亲(viewpager)拦截

       final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_MOVE -> {
                Log.i("GUOHENG", "CustomSeekBar onTouchEvent"+ "ACTION_MOVE")
......
......
               parent.requestDisallowInterceptTouchEvent(true)
            }

此刻就能正常滑动了。


0t5lo-x76hp.gif

文章参考 # [Android事件分发机制详解:史上最全面、最易懂]

你可能感兴趣的:(滑动冲突1)