美文网首页
SwipeLayout解析-事件分发

SwipeLayout解析-事件分发

作者: BooQin | 来源:发表于2018-07-21 20:54 被阅读138次

前言

在上一篇《SwipeLayout解析-动画篇》中,我们了解到其动画的实现主要依赖于ViewDragHelper来完成。而本文将结合《View事件分发浅析》,来分析在SwipeLayout中是如何处理事件的冲突的。

SwipeLayout事件冲突处理

在SwipeLayout中,对其内部的子View做滑动的处理,即重写onTouchEvent方法,onTouchEvent方法的部分代码如下:

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //1.如果屏蔽了抽屉效果,直接运行父类的onTouchEvent并返回
        if (!isSwipeEnabled()) {
            return super.onTouchEvent(event);
        }

        //2.处理点击事件
        int action = event.getActionMasked();
        gestureDetector.onTouchEvent(event);

        //3.交由DragHelper,完成子View的动画更新
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mDragHelper.processTouchEvent(event);
                sX = event.getRawX();
                sY = event.getRawY();

            case MotionEvent.ACTION_MOVE: {
                //the drag state and the direction are already judged at onInterceptTouchEvent
                checkCanDrag(event);
                if (mIsBeingDragged) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    mDragHelper.processTouchEvent(event);
                }
                break;
            }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mIsBeingDragged = false;
                mDragHelper.processTouchEvent(event);
                break;

            default://handle other action, such as ACTION_POINTER_DOWN/UP
                mDragHelper.processTouchEvent(event);
        }

        return super.onTouchEvent(event) || mIsBeingDragged || action == MotionEvent.ACTION_DOWN;
    }

在该方法中主要分为三个部分,首先,会去判断是否开启了抽屉效果,如果未开启就会去调用父类View下的onTouchEvent,否则就执行SwipeLayout下的处理方法,然后,调用gestureDetector中的方法,这里主要做了对点击事件的处理,比如click,longClick等监听事件的响应,最后会做一个Switch判断,其调用了DragHelper的processTouchEvent,即将手势事件传递到ViewDragHelper中处理。
  在onTouchEvent中,如果要消耗点击事件,就需要在返回值中设置为true,而在SwipeLayout中重写的onTouchEvent,我们可以看到由三个值决定,并且是或关系,对于ACTION_DOWN事件,会返回true值进行事件的处理和消耗,因此,在开启了SwipeEnabled的情况下,当事件传递到SwipeLayout上时,所有的手势都会在onTouchEvent被消耗。
  在SwipeLayout重载了onTouchEvent后,我们可以在Activity中使用了,如果我们仅仅是单独使用SwipeLayout,会发现一起都很顺利,左滑,下滑等等操作都能实现对应的效果。但是,当我们需要嵌套SwipeLayout的时候,或者在其子View中添加点击事件时,就会发现对于SwipeLayout的手势动画全部无效了,这就是事件冲突了。我们需要使用onInterceptTouchEvent结合oequestDisallowInterceptTouchEvent方法组合来解决该类问题。
  通过View事件分发浅析,我们可以知道,如果ViewGroup的onInterceptTouchEvent方法返回了True,那么该事件就会被ViewGroup拦截,交由其onTouchEvent处理,并且不会再传递到子View中。因此,我们可以再SwipeLayout中设置该方法来达到分事件拦截的目的。该控件的作者在该方法做了如下实现:

    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if(mForceInterceptAble){
            return mForceIntercept;
        }
        if (!isSwipeEnabled()) {
            return false;
        }
        if (mClickToClose && getOpenStatus() == Status.Open && isTouchOnSurface(ev)) {
            return true;
        }
        for (SwipeDenier denier : mSwipeDeniers) {
            if (denier != null && denier.shouldDenySwipe(ev)) {
                return false;
            }
        }
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDragHelper.processTouchEvent(ev);
                mIsBeingDragged = false;
                sX = ev.getRawX();
                sY = ev.getRawY();
                //if the swipe is in middle state(scrolling), should intercept the touch
                if (getOpenStatus() == Status.Middle) {
                    mIsBeingDragged = true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                boolean beforeCheck = mIsBeingDragged;
                checkCanDrag(ev);
                if (mIsBeingDragged) {
                    ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }
                if (!beforeCheck && mIsBeingDragged) {
                    //let children has one chance to catch the touch, and request the swipe not intercept
                    //useful when swipeLayout wrap a swipeLayout or other gestural layout
                    return false;
                }
                break;

            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                mIsBeingDragged = false;
                mDragHelper.processTouchEvent(ev);
                break;
            default://handle other action, such as ACTION_POINTER_DOWN/UP
                mDragHelper.processTouchEvent(ev);
        }
        return mIsBeingDragged;
    }

首先,根据代码可以知道,在不同的手势事件下有不同返回值。以SwipeLaytout A嵌套SwipeLayout B为例。
  Down事件:该事件我们必须返回false,不然所有的事件都无法传递到子View。而在SwipeLayout中只有在动画执行中返回true。
  MOVE事件:事件处理的重点在于MOVE事件,可以通过该手势来判断是点击还是拖拽操作,在SwipeLayout中,通过beforeCheck和mIsBeingDragged两个标志为来决定是否拦截,从代码可知,前者是mIsBeingDragged的初始值,当用户滑动的时候,mIsBeingDragged会在checkCanDrag(ev)被置为True,然后就会调用requestDisallowInterceptTouchEvent方法,该方法可以使父类的onInterceptTouchEvent值无效,紧接着做了一个判断:

    if (!beforeCheck && mIsBeingDragged) {
        //let children has one chance to catch the touch, and request the swipe not intercept
        //useful when swipeLayout wrap a swipeLayout or other gestural layout
        return false;
    }

该方法是解决嵌套冲突的关键,其核心思想是在第一次判断为滑动的情况下不进行拦截,继续传递,所以SwipeLayout A在第一次Move事件中不进行拦截,将事件传递到SwipeLayout B(下一层View)中,这样便为SwipeLayout B提供了一次执行requestDisallowInterceptTouchEvent方法的机会,也以此来解决了嵌套的冲突情况。
  UP和CANCEL事件:该事件并未做特殊处理,只是将事件交由ViewDragHelper处理。
  通过以上的设置,就可以解决嵌套的事件冲突了,效果如下图:

SwipeLayout_GIF

但是在实际的使用过程中,发现可能出现滑动混乱的现象,在代码中的Down事件下mIsBeingDragged被置为True,这就导致了所有事件被拦截,具体原因初步判断时SwipeLayout的OpenStatus没有正确设置。
  关于SwipeLayout控件,我Fork到了自己的github上(ZhangFengG/AndroidSwipeLayout),并做了一些设置项的修改,添加了对onInterceptTouchEvent的手动设置,这样可以更直观的观察该方法在ViewGroup中的作用:

SwipeLayout_Main

相关资料

end

相关文章

  • SwipeLayout解析-事件分发

    前言 在上一篇《SwipeLayout解析-动画篇》中,我们了解到其动画的实现主要依赖于ViewDragHelpe...

  • # View事件分发(二)

    View事件分发(二) 事件分发的源码解析 Activity对点击事件的分发过程 点击事件用MotionEvent...

  • Android点击事件分发

    TouchEventDispatchDemo 点击事件分发 基本事件分发为: 源码解析 Activity Acti...

  • Android事件分发机制

    转发一份写的不错的博客[Android事件分发机制解析Android事件分发机制解析 - 简书 (jianshu....

  • Android事件分发机制完全解析

    Android事件分发机制完全解析,带你从源码的角度彻底理解(上)Android事件分发机制完全解析,带你从源码的...

  • 有趣的链接

    Android事件分析 可能是讲解Android事件分发最好的文章 Android事件分发机制完全解析,带你从源码...

  • View事件分发相关结论的源码解析

    View事件分发相关结论的源码解析 了解过View事件分发源码的同学或多或少都知道一些事件分发的相关结论,比如某个...

  • Android事件分发学习路线

    Android事件分发机制,大表哥带你慢慢深入(图解很赞) Android View 事件分发机制源码解析(鸿洋写...

  • 【Android源码】View的事件分发机制

    Android事件分发完全解析之事件从何而来 Activity的事件分发过程 关于事件是如何而来的,可以参考上面的...

  • Android 事件分发机制

    搬砖参考一文读懂Android View事件分发机制Android事件分发机制完全解析,带你从源码的角度彻底理解(...

网友评论

      本文标题:SwipeLayout解析-事件分发

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