android开发之事件处理(祥谈)

当你点击屏幕的时候最开始得到的事件是最外层的ViewRoot–>ViewGroup….这样依次传递过来的,在这里的ViewRoot就直接忽视了,那就来看看ViewGroup,我们知道布局文件的最外层都是使用五大布局包裹着的,而五大布局又是ViewGroup的子类,因此最开始拿到事件的就是ViewGroup(我们这里直接忽视了ViewRoot),ViewGroup得到事件之后,触发的第一个方法就是dospatchTouchEvent(ev)分发事件,那么在该方法中都干了些什么呢?我们来到源码:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {

    ...省略

    //记录当前点击的触摸事件的坐标,同时得到一个矩形用于搜索锁定触摸事件的坐标的范围
    final int action = ev.getAction();
    final float xf = ev.getX();
    final float yf = ev.getY();
    final float scrolledXFloat = xf + mScrollX;
    final float scrolledYFloat = yf + mScrollY;
    final Rect frame = mTempRect;

     //是否是触发的DOWN事件
    if (action == MotionEvent.ACTION_DOWN) {

        ...省略

        // 没有进行任何的事件拦截处理
        if (disallowIntercept || !onInterceptTouchEvent(ev)) {
            // 再次确定是DOWN事件(只为了让自己放心)
            ev.setAction(MotionEvent.ACTION_DOWN);

            ...省略

        }
    }

    ...省略

}

不容置疑的是点击事件一开始绝壁是ACTION_DOWN事件,当然Google工程师还是在触发的第一个方法(dispatchTouchEvent())中进行了两次判断该点击事件是否是ACTION_DOWN,因此一旦事件的发生就会执行dispatchTouchEvent()方法,那么在该方法中第一件事就是记录当前点击的触摸事件的坐标,同时得到一个矩形用于搜索锁定触摸事件的坐标的范围,然后再进行是否是DOWN事件判断。

//是否是触发的DOWN事件
if (action == MotionEvent.ACTION_DOWN) {

    ...省略

    // 没有进行任何的事件拦截处理
    if (disallowIntercept || !onInterceptTouchEvent(ev)) {//调用onInterceptTouchEvent()方法
        // 再次确定是DOWN事件(只为了让自己放心)
        ev.setAction(MotionEvent.ACTION_DOWN);

        ...省略

    }
}

如果是ACTION_DOWN事件,那么在当前的dispatchTouchEvent()方法中去调用onInterceptTouchEvent(ev)中断事件方法。

public boolean onInterceptTouchEvent(MotionEvent ev) {
    return false;
}

如果onInterceptTouchEvent(ev)方法返回的是false(默认返回false),那么就说明在当前ViewGroup类中不进行中断事件,那么当前类的onTouchEvent()处理事件方法就不会去执行(而是交给子View去解决)。

onInterceptTouchEvent(ev)方法返回的是false(交给子View去解决,那么交给子View解决就得得到所有的子View):if条件满足,继续执行下面的代码,把当前点击的触摸事件的坐标记录下来,并得到该ViewGroup类中所有的子View,存储到数组中,并得到子View的总个数。

//我们知道我们想要分派事件,找到可以处理事件的孩子
for (int i = count - 1; i >= 0; i--) {
    final View child = children[i];
    if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
        child.getHitRect(frame);
        if (frame.contains(scrolledXInt, scrolledYInt)) {
            final float xc = scrolledXFloat - child.mLeft;
            final float yc = scrolledYFloat - child.mTop;
            ev.setLocation(xc, yc);
            child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
            if (child.dispatchTouchEvent(ev))  {
                // 事件处理,现在我们有了目标
                mMotionTarget = child;
                return true;
            }
        }
    }
}

循环遍历存储当前ViewGroup中所有的子View数组,循环通过变量i的值得到对应角标的子View,首先判断该子View能否被点击。

如果子View能被点击,那就说明它有可能被点击了,如果连点击都不能点击,那么DOWN事件就绝壁不会发生。因此既然有可能被点击了,那么就得通过子View得到矩形(搜索锁定触摸事件的坐标的范围),如果该矩形有包含当前点击的触摸事件的坐标,那么就再去判断一下,子View调用dispatcheTouchEvent(ev)方法,这里要特别注意:如果子View还是ViewGroup类的子View,那么子View调用的dispatcheTouchEvent(ev)方法就是本dispatcheTouchEvent(ev)方法,那么执行的过程也是一样的。这样一次一次的调用dispatcheTouchEvent(ev)方法,其实这样的设计思路很简单只要你是ViewGroup类或者其子类,那么我就一直寻找其子View的子View甚至是子View的子View,就这样一直找到最后一个子View并且该子View是View,而不是ViewGroup,是View就不会调用ViewGroup的dispatcheTouchEvent(ev)方法,而是去调用View中的dispatcheTouchEvent(ev),而在View中的dispatcheTouchEvent(ev)方法中又进行了是否设置了onTouchListener对象,OnTouchListener.onTouch(this, event)返回的是false or true之类的判断。

//View类的dispatchTouchEvent(ev):

public boolean dispatchTouchEvent(MotionEvent event) {
    if (!onFilterTouchEventForSecurity(event)) {
        return false;
    }

    if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) {
        return true;
    }
    return onTouchEvent(event);
}

如果为true,说明这个子子…View要了这个事件,那么View中的dispatcheTouchEvent(ev)方法也就整体返回true,这样我们又来到了ViewGroup中的dispatcheTouchEvent(ev)方法中的子View调用dispatcheTouchEvent(ev)得到的值也就为true,这样的话就继续执行下面的代码,把这个子子…View赋为事件目标,这样所有的事件都由子子…View来处理。

if (child.dispatchTouchEvent(ev))  {
    // 事件处理,现在我们有了目标
    mMotionTarget = child;
    return true;
}

如果OnTouchListener.onTouch(this, event)返回的是false,我也不怕,因为我还得执行onTouchEvent(event)方法,如果子子…View中的onTouchEvent(event)方法返回true,这也说明这个事件我子子…View要了,那么View中的dispatcheTouchEvent(ev)方法也就整体返回true,这样我们又来到了ViewGroup中的dispatcheTouchEvent(ev)方法中的子View调用dispatcheTouchEvent(ev)得到的值也就为true,这样的话就继续执行下面的代码,把这个子子…View赋为事件目标,这样所有的事件都由子子…View来处理。

if (!disallowIntercept && onInterceptTouchEvent(ev)) {
    final float xc = scrolledXFloat - (float) target.mLeft;
    final float yc = scrolledYFloat - (float) target.mTop;
    mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
    ev.setAction(MotionEvent.ACTION_CANCEL);
    ev.setLocation(xc, yc);
    if (!target.dispatchTouchEvent(ev)) {
        // target didn't handle ACTION_CANCEL. not much we can do
        // but they should have.
    }
    // clear the target
    mMotionTarget = null;
    // Don't dispatch this event to our own view, because we already
    // saw it when intercepting; we just want to give the following
    // event to the normal onTouchEvent().
    return true;
}

...省略

return target.dispatchTouchEvent(ev);//所有的事件交给子View来处理

如果View中的dispatcheTouchEvent()方法中的代码执行完之后都返回false,就说明子子…View不要这个事件,那么这个事件又返回给了其父View,这样我们又来到了ViewGroup中的dispatcheTouchEvent(ev)方法中的子View调用dispatcheTouchEvent(ev)得到的值也就为false,这样的话事件目标没有赋值,为空。

那么,就又来到了ViewGroup类中,既然事件目标(mMotionTarget)没有赋值,为空了,那么赋值给target也就为null,既然target为空,也就满足了if(target == null)的if语句,执行其中的方法,最终调用了父类的dispatchTouchEvent(ev),由其父View来处理。

if (target == null) {
    //我们没有目标(孩子不处理这些事件),这意味着我们自己来处理
    ev.setLocation(xf, yf);
    if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
        ev.setAction(MotionEvent.ACTION_CANCEL);
        mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
    }
    return super.dispatchTouchEvent(ev); //所有的事件交给父View来处理
}

如果,在onInterceptTouchEvent()一上来就返回true(父View中进行了该方法的覆写),意味着父View进行了事件的拦截,其身要了这些事件了,那么,代码会直接来到这:

if (target == null) {
    //我们没有目标(孩子不处理这些事件),这意味着我们自己来处理
    ev.setLocation(xf, yf);
    if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
        ev.setAction(MotionEvent.ACTION_CANCEL);
        mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
    }
    return super.dispatchTouchEvent(ev); //所有的事件交给父View来处理
}

另外,附上两张图,将上面的理论知识进行了图片的展示,以便大家更好的掌握事件处理!

图一:

图二:

android开发之事件处理(祥谈)_第1张图片

到此为止,事件的处理2.3源码分析我就讲的这,谢谢您能浏览到最后,如果您觉得本人写的不错,请给我一个好评,谢谢!

你可能感兴趣的:(android,事件处理,事件分发,事件拦截)