• [UI]抽屉菜单DrawerLayout分析(二)


    继续分析DrawerLayout的手势分发部分

    谈到手势分发,这本身就是个好话题,DrawerLayout作为继承自ViewGroup得布局他可以拦截手势也可以分发给子view,也就是在onInterceptTouchEvent中做的操作,但是他的下面还有一个onTouchEvent方法,先看哪个呢?追溯代码我们可以知道ViewGroup继承自View,而onTouchEvent是View的方法

    NewImage

    我们还是先花点时间把两者的关系先确认再继续。

    onInterceptTouchEvent和onTouchEvent---鸡和蛋?

    定位到ViewGroup,可以发现onInterceptTouchEvent分定义如下,从它前面一段非常长的注释就可以看出其重要性和复杂,默认的返回是false

    /**

     * Implement this method to intercept all touch screen motion events.  This

     * allows you to watch events as they are dispatched to your children, and

     * take ownership of the current gesture at any point.

     *

     * <p>Using this function takes some care, as it has a fairly complicated

     * interaction with {@link View#onTouchEvent(MotionEvent)

     * View.onTouchEvent(MotionEvent)}, and using it requires implementing

     * that method as well as this one in the correct way.  Events will be

     * received in the following order:

     *

     * <ol>

     * <li> You will receive the down event here.

     * <li> The down event will be handled either by a child of this view

     * group, or given to your own onTouchEvent() method to handle; this means

     * you should implement onTouchEvent() to return true, so you will

     * continue to see the rest of the gesture (instead of looking for

     * a parent view to handle it).  Also, by returning true from

     * onTouchEvent(), you will not receive any following

     * events in onInterceptTouchEvent() and all touch processing must

     * happen in onTouchEvent() like normal.

     * <li> For as long as you return false from this function, each following

     * event (up to and including the final up) will be delivered first here

     * and then to the target's onTouchEvent().

     * <li> If you return true from here, you will not receive any

     * following events: the target view will receive the same event but

     * with the action {@link MotionEvent#ACTION_CANCEL}, and all further

     * events will be delivered to your onTouchEvent() method and no longer

     * appear here.

     * </ol>

     *

     * @param ev The motion event being dispatched down the hierarchy.

     * @return Return true to steal motion events from the children and have

     * them dispatched to this ViewGroup through onTouchEvent().

     * The current target will receive an ACTION_CANCEL event, and no further

     * messages will be delivered here.

     */

    public boolean onInterceptTouchEvent(MotionEvent ev) {

        return false;

    }

     

    前两段告诉我们,复写onInterceptTouchEvent方法,可以实现监听所有的动作事件MotionEvent,在向子view传递事件前做我们需要的操作,当然这指的是和这个viewgroup相关的事件;同时我们需要慎重处理该函数,因为他和onTouchEvent关系非常紧密,下面是事件接收的顺序:

    首先接收的的事按下事件,down事件,他可以被view处理也可以在自身的onTouchEvent里处理,所以实现onTouchEvent并且返回true,这样onTouchEvent继续才能收到down之后的其他事件,同时onInterceptTouchEvent不会在收到后续事件,因为已经转移到onTouchEvent处理了。

    那么什么时候onInterceptTouchEvent会把后续事件转移到他的onTouchEvent呢?这取决于onInterceptTouchEvent的返回值,如果返回false,所有事件都会先分发到这里,然后再到目标view的onTouchEvent;相反如果返回true,那么onInterceptTouchEvent将不再收到后续事件,并且目标view会收到cancel事件,接着自身的onTouchEvent几首后续的事件。

    这其实从名字来看是比较好理解的onInterceptTouchEvent表示在截取触摸事件的被调用的方法,既然是截取就可以直接吧事件截下来后不再往后传递,这是就是上面的第二种情况,返回true,即我们自己消耗了触摸事件,子view将没有机会得到唤醒。

    OnInterceptTouchEvent

    大致意思就是如果希望自身消耗掉改事件就可以直接返回true,这一点和onTouchEvent的返回类似目的。

    博客园有篇文章对这些事件分发做了很好的分析:http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html

    详细的阐述了了dispatchTouchEvent,onInterceptTouchEvent以及onTouchEvent之间的关系

    现在我们回过头来看DrawerLayout里的分发是如何写的:

    重写了后面两个方法,先看onInterceptTouchEvent:

    @Override

    public boolean onInterceptTouchEvent(MotionEvent ev) {

        final int action = MotionEventCompat.getActionMasked(ev);

     

        // "|" used deliberately here; both methods should be invoked.

        final boolean interceptForDrag = mLeftDragger.shouldInterceptTouchEvent(ev) |

                mRightDragger.shouldInterceptTouchEvent(ev);

     

        boolean interceptForTap = false;

     

        switch (action) {

            case MotionEvent.ACTION_DOWN: {

                final float x = ev.getX();

                final float y = ev.getY();

                mInitialMotionX = x;

                mInitialMotionY = y;

                if (mScrimOpacity > 0 &&

                        isContentView(mLeftDragger.findTopChildUnder((int) x, (int) y))) {

                    interceptForTap = true;

                }

                mDisallowInterceptRequested = false;

                mChildrenCanceledTouch = false;

                break;

            }

     

            case MotionEvent.ACTION_MOVE: {

                // If we cross the touch slop, don't perform the delayed peek for an edge touch.

                if (mLeftDragger.checkTouchSlop(ViewDragHelper.DIRECTION_ALL)) {

                    mLeftCallback.removeCallbacks();

                    mRightCallback.removeCallbacks();

                }

                break;

            }

     

            case MotionEvent.ACTION_CANCEL:

            case MotionEvent.ACTION_UP: {

                closeDrawers(true);

                mDisallowInterceptRequested = false;

                mChildrenCanceledTouch = false;

            }

        }

     

        return interceptForDrag || interceptForTap || hasPeekingDrawer() || mChildrenCanceledTouch;

    }

     

    1.首先从touch event里面获取当前具体的action动作,MotionEventCompat.getActionMasked(ev),内部实际上做了一次按位于操作event.getAction() & ACTION_MASK;

    2.检查当前是否满足截取drag状态,用于决定onInterceptTouchEvent返回值,这里有个注解说是故意用了|或,而不是||或,两者区别在于||只要第一个条件满足就不在执行第二个检查,二|不同,无论如何都会将两个条件检查一遍;

    3.接下来是几个case,根据当前的action做处理;

    ACTION_DOWN,当按下时记录按下点的x,y坐标值,根据条件设置当前是否满足tap状态,具体条件有两个,一是mScrimOpacity,表示子view中在屏幕上占据的最大宽度(0-1),二时根据坐标点的位置取得改点对应的最上层view对象,如果是预定义的content view即DrawerLayout里的主内容展示view,也就是同时满足view在屏幕上且点击的位置直接落在了content view上。

    ACTION_MOVE,当手按下后开始在屏幕上移动时,如果垂直和水平上的位移差量达到了drag helper的阀值则一处左右两边的回调接口

    ACTION_CANCLE和ACTION_UP,手势结束后,关闭菜单

    最后结合几个状态来那个来决定onInterceptTouchEvent返回true还是false,

    未完待续

  • 相关阅读:
    大三进度条01
    阅读笔记01
    问题账户需求分析
    《人月神话》阅读笔记02
    《人月神话》阅读笔记01
    每周总结(第五周)
    《构建之法》阅读笔记06
    《构建之法》阅读笔记05
    《构建之法》阅读笔记04
    《构建之法》阅读笔记03
  • 原文地址:https://www.cnblogs.com/avenwu/p/3675838.html
Copyright © 2020-2023  润新知