Android——事件分发机制

概念

Touch事件分发中的对象:ViewGroup和View。

View的相关事件只有两个:dispatchTouchEvent、onTouchEvent。

ViewGroup的相关事件有三个:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。

触摸事件的动作:ACTION_DOWN、ACTION_MOVE、ACTION_UP

事件序列:ACTION_DOWN-->若干个ACTION_MOVE-->ACTION_UP(包含多个触摸事件的动作)

距离常量(TouchSlop):系统中预定义的用来判断用户手指在屏幕上的滑动是否是一个ACTION_MOVE动作

情景测试

Activity层和View层测试代码如下(只是将打印日志区分)

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.e(Tag,"onTouchEvent_act");
    switch (event.getAction())
    {
        case MotionEvent.ACTION_DOWN:
            Log.e(Tag,"onTouchEvent_act_down");
            break; 
        case MotionEvent.ACTION_MOVE:
            Log.e(Tag,"onTouchEvent_act_move");
            break;
        case MotionEvent.ACTION_UP:
            Log.e(Tag,"onTouchEvent_act_up");
            break;
    }
    return super.onTouchEvent(event);
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Log.e(Tag,"dispatchTouchEvent_act");
    switch (ev.getAction())
    {
        case MotionEvent.ACTION_DOWN:
            Log.e(Tag,"dispatchTouchEvent_act_down");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.e(Tag,"dispatchTouchEvent_act_move");
            break;
        case MotionEvent.ACTION_UP:
            Log.e(Tag,"dispatchTouchEvent_act_up");
            break;
    }
    return super.dispatchTouchEvent(ev);
}

ViewGroup层测试代码同上,多了一个onInterceptTouchEvent方法

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    Log.e(Tag,"onInterceptTouchEvent_linear");
    switch (ev.getAction())
    {
        case MotionEvent.ACTION_DOWN:
            Log.e(Tag,"onInterceptTouchEvent_linear_down");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.e(Tag,"onInterceptTouchEvent_linear_move");
            break;
        case MotionEvent.ACTION_UP:
            Log.e(Tag,"onInterceptTouchEvent_linear_up");
            break;
    }
    return super.onInterceptTouchEvent(ev);
}

情景一:所有的操作都保持默认,点击View区域;结果如下

07-25 09:55:00.042 6764-6764/com.roi.audio2video E/rrrrrrrrr:

    dispatchTouchEvent_act
    dispatchTouchEvent_act_down
    dispatchTouchEvent_linear
    dispatchTouchEvent_linear_down
    onInterceptTouchEvent_linear
    onInterceptTouchEvent_linear_down
    dispatchTouchEvent_view
    dispatchTouchEvent_view_down
    onTouchEvent_view
    onTouchEvent_view_down
    onTouchEvent_linear
    onTouchEvent_linear_down
    onTouchEvent_act
    onTouchEvent_act_down

07-25 09:55:00.089 6764-6764/com.roi.audio2video E/rrrrrrrrr:

    dispatchTouchEvent_act
    dispatchTouchEvent_act_move
    onTouchEvent_act
    onTouchEvent_act_move

07-25 09:55:00.122 6764-6764/com.roi.audio2video E/rrrrrrrrr:

    dispatchTouchEvent_act
    dispatchTouchEvent_act_move
    onTouchEvent_act
    onTouchEvent_act_move

07-25 09:55:00.189 6764-6764/com.roi.audio2video E/rrrrrrrrr:

    dispatchTouchEvent_act
    dispatchTouchEvent_act_up
    onTouchEvent_act
    onTouchEvent_act_up

分析:可以看到一个事件列包含多个事件的传递

1,事件传递是拿事件列的Down事件作为令牌,通过第一次事件传递判断谁得到令牌使用权;接下来就通过再次事件传递将事件列的其它事件(Move,Up等事件)直接传递给该View;上面例子中的事件没有被消费掉,所以最终消费权又回到了activity层;调用了Activity的 onTouchEvent_act_move ,onTouchEvent_act_up

2,事件传递的过程是  先 dispatch(Activity外层 ——> ViewGroup中层——>View里层)

                                     再 onTouch(Activity外层 <—— ViewGroup中层<——View里层)

注意:ViewGroup中如果存在onIntercept,在dispatch的过程中在ViewGroup层调用dispatchTouchEvent()之后会调用onInterceptTouchEvent();

 事件传递的过程是      先 dispatch(Activity外层 —> ViewGroup中层(派遣) —> ViewGroup中层(拦截)—>View里层)

                                    再 onTouch(Activity外层 <—— ViewGroup中层<——View里层)

情景二:改变dispatchTouchEvent的返回结果;进行观察

1,修改Activity外层dispatchTouchEvent返回为true,其他不变

07-25 11:00:23.312 16925-16925/com.roi.audio2video E/rrrrrrrrr:

    dispatchTouchEvent_act
    dispatchTouchEvent_act_down
07-25 11:00:23.392 16925-16925/com.roi.audio2video E/rrrrrrrrr:

    dispatchTouchEvent_act
    dispatchTouchEvent_act_move
07-25 11:00:23.472 16925-16925/com.roi.audio2video E/rrrrrrrrr:

   dispatchTouchEvent_act
    dispatchTouchEvent_act_move
07-25 11:00:23.482 16925-16925/com.roi.audio2video E/rrrrrrrrr:

    dispatchTouchEvent_act
    dispatchTouchEvent_act_up

分析:事件派发在activity层中止! Activity层的onTouch()没有被触发。

2,在上面1的基础上,将onTouchEvent()返回true

07-25 11:28:41.472 21808-21808/com.roi.audio2video E/rrrrrrrrr:

   dispatchTouchEvent_act
    dispatchTouchEvent_act_down
07-25 11:28:41.592 21808-21808/com.roi.audio2video E/rrrrrrrrr:

   dispatchTouchEvent_act
    dispatchTouchEvent_act_move
07-25 11:28:41.602 21808-21808/com.roi.audio2video E/rrrrrrrrr:

   dispatchTouchEvent_act
    dispatchTouchEvent_act_move
07-25 11:28:41.622 21808-21808/com.roi.audio2video E/rrrrrrrrr:

    dispatchTouchEvent_act
    dispatchTouchEvent_act_up

分析:返回的结果和上面的一样;还是要看一下Activity中的源码

/**
 * Called to process touch screen events.  You can override this to
 * intercept all touch screen events before they are dispatched to the
 * window.  Be sure to call this implementation for touch screen events
 * that should be handled normally.
 *
 * @param ev The touch screen event.
 *
 * @return boolean Return true if this event was consumed.
 */
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

分析:当我们重写dispatchTouchEvent()时,是没有调用onTouch()方法的;也就是说ontouchEvent()本身是在dispatchTouchEvent()内部调用的;重写的时候直接返回true或者false当然是不能触发onTouch()方法的;

情景三:改变onTouchEvent的返回结果;进行观察

1,修改Activity层的onTouchEvent返回值为true;其他不变!

07-25 11:54:43.692 25835-25835/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_down
    dispatchTouchEvent_linear
    dispatchTouchEvent_linear_down
    onInterceptTouchEvent_linear
    onInterceptTouchEvent_linear_down
    dispatchTouchEvent_view
    dispatchTouchEvent_view_down
    onTouchEvent_view
    onTouchEvent_view_down
    onTouchEvent_linear
    onTouchEvent_linear_down
    onTouchEvent_act
    onTouchEvent_act_down
07-25 11:54:43.762 25835-25835/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_move
    onTouchEvent_act
    onTouchEvent_act_move
07-25 11:54:43.782 25835-25835/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_move
    onTouchEvent_act
    onTouchEvent_act_move
07-25 11:54:43.782 25835-25835/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_up
    onTouchEvent_act
    onTouchEvent_act_up

分析:可以很明显的发现,down事件是贯穿了整个传递的过程;但是事件列的其他事件(up,move)就直接送到activity外层的onTouchEvent()中被消费;而不会向内层View进行传递;

2,修改ViewGroup层的onTouchEvent返回值为true;其他不变!

07-25 13:44:22.282 5233-5233/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_down
    dispatchTouchEvent_linear
    dispatchTouchEvent_linear_down
    onInterceptTouchEvent_linear
    onInterceptTouchEvent_linear_down
    dispatchTouchEvent_view
    dispatchTouchEvent_view_down
    onTouchEvent_view
    onTouchEvent_view_down
    onTouchEvent_linear
    onTouchEvent_linear_down
07-25 13:44:22.392 5233-5233/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_move
    dispatchTouchEvent_linear
    dispatchTouchEvent_linear_move
    onTouchEvent_linear
    onTouchEvent_linear_move
07-25 13:44:22.592 5233-5233/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_move
    dispatchTouchEvent_linear
    dispatchTouchEvent_linear_move
    onTouchEvent_linear
    onTouchEvent_linear_move
07-25 13:44:22.612 5233-5233/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_up
    dispatchTouchEvent_linear
    dispatchTouchEvent_linear_up
    onTouchEvent_linear
    onTouchEvent_linear_up

分析:因为ViewGroup层消费掉了该事件,down事件在ViewGroup的onTouchEvent()中中断,停止向上层Activity中传递;事件列的其他事件(up,move)也跟随down事件建立的连接传到ViewGroup层中;不会向内层View进行传递;

情景四:改变onInterceptTouchEvent()的返回结果;进行观察

1,修改ViewGroup层的onInterceptTouchEvent()返回值为true;其他不变!

07-25 16:21:44.082 30048-30048/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_down
    dispatchTouchEvent_linear
    dispatchTouchEvent_linear_down
    onInterceptTouchEvent_linear
    onInterceptTouchEvent_linear_down
    onTouchEvent_linear
    onTouchEvent_linear_down
    onTouchEvent_act
    onTouchEvent_act_down
07-25 16:21:44.162 30048-30048/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_move
    onTouchEvent_act
    onTouchEvent_act_move
07-25 16:21:44.172 30048-30048/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_move
    onTouchEvent_act
    onTouchEvent_act_move
07-25 16:21:44.212 30048-30048/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_up
    onTouchEvent_act
    onTouchEvent_act_up

分析:当中间层ViewGroup对事件进行拦截之后;dispatchTouchEvent中断,无法向下传到View层;然后从ViewGroup开始onTouchEvent往外层的Activity进行传递;而onTouchEvent因为dispatchTouchEvent中断而无法到达最里层的View中

2,修改ViewGroup层的onInterceptTouchEvent()只对最里层view的纵向滑动进行拦截;并将里层View的onTouchEvent改为true!

private float startX,startY;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    Log.e(Tag,"onInterceptTouchEvent_linear");
    switch (ev.getAction())
    {
        case MotionEvent.ACTION_DOWN:
            startX = ev.getX();
            startY = ev.getY();
            Log.e(Tag,"onInterceptTouchEvent_linear_down");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.e(Tag,"onInterceptTouchEvent_linear_move");
            float dexX = Math.abs(ev.getX()-startX);
            float dexY = Math.abs(ev.getY()-startY);

            if (dexY > dexX && dexY > scaledTouchSlop)
            {
                Log.e(Tag,"拦截纵向滑动");
                return true;
            }
            break;
        case MotionEvent.ACTION_UP:
            Log.e(Tag,"onInterceptTouchEvent_linear_up");
            break;
    }
    return super.onInterceptTouchEvent(ev);
}

在View上面纵向滑动,先看一下down事件的传递,因为View的onTouchEvent为true;所以Down事件到View层不会往上传递

07-25 16:46:46.622 3074-3074/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_down
    dispatchTouchEvent_linear
    dispatchTouchEvent_linear_down
    onInterceptTouchEvent_linear
    onInterceptTouchEvent_linear_down
    dispatchTouchEvent_view
    dispatchTouchEvent_view_down
    onTouchEvent_view
    onTouchEvent_view_down

在看一下事件列里面的其他事件的传递(move事件开始的时候是正常传递到里层的View,被拦截之后就无法到达了,从中间层直接往上传到Activity层了)

07-25 16:46:46.692 3074-3074/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_move
    dispatchTouchEvent_linear
    dispatchTouchEvent_linear_move
    onInterceptTouchEvent_linear
    onInterceptTouchEvent_linear_move
    dispatchTouchEvent_view
    dispatchTouchEvent_view_move
    onTouchEvent_view
    onTouchEvent_view_move
07-25 16:46:46.712 3074-3074/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_move
    dispatchTouchEvent_linear
    dispatchTouchEvent_linear_move
    onInterceptTouchEvent_linear
    onInterceptTouchEvent_linear_move
    拦截纵向滑动
    dispatchTouchEvent_view
    onTouchEvent_view
07-25 16:46:46.732 3074-3074/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
    dispatchTouchEvent_act_move
    dispatchTouchEvent_linear
    dispatchTouchEvent_linear_move
    onTouchEvent_linear
    onTouchEvent_linear_move
    onTouchEvent_act
    onTouchEvent_act_move

猜你喜欢

转载自blog.csdn.net/qq_34601429/article/details/81167086