View的事件传递

先创建个ViewGroupA和ViewB,然后我们来看看事件是怎么传递的。

public class ViewGroupA extends RelativeLayout {
 ...
    /**
     * 传递事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d("touch", "ViewGroupA : dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);

    }

    /**
     * 拦截事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d("touch", "ViewGroupA :   onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);
    }

    /**
     * 处理事件
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("touch", "ViewGroupA :   onTouchEvent");
        return super.onTouchEvent(event);
    }
}

这里没有直接继承ViewGroup 而是RelativeLayout,主要是为好布局。其实一样的RelativeLayout就是继承的ViewGroup。

public class ViewB extends View {
....
  /**
     * 事件传递
     *
     * @param event
     * @return
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d("touch", "ViewB : dispatchTouchEvent");
        return super.dispatchTouchEvent(event);
    }

/**
     * 事件处理
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("touch", "ViewB : onTouchEvent");
        return super.onTouchEvent(event);
    }

}

布局文件:




    

        
    


显示效果:

View的事件传递_第1张图片
image.png

点击蓝色区域即ViewB,然后我们看下日志输出:

 D/touch: ViewGroupA : dispatchTouchEvent
 D/touch: ViewGroupA :   onInterceptTouchEvent
 D/touch: ViewB : dispatchTouchEvent
 D/touch: ViewB : onTouchEvent
 D/touch: ViewGroupA :   onTouchEvent

从日志输出顺序可以看出,事件由Activity-->ViewGroupA 如果ViewGroupA不拦截
事件由ViewGroupA-->ViewB 当前ViewB为最上面一层View,如果ViewB不处理,那么ViewB就将事件又返回给ViewGroupB 交由他的onTouchEvent处理

试下ViewGroupA将事件拦截了,看看日志输出:
修改ViewGroupA代码:

    /**
     * 拦截事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d("touch", "ViewGroupA :   onInterceptTouchEvent");
        return true;
    }

日志输出:

 D/touch: ViewGroupA : dispatchTouchEvent
 D/touch: ViewGroupA :   onInterceptTouchEvent
 D/touch: ViewGroupA :   onTouchEvent

我们发现没有关于ViewB的输出,这说明我们的事件拦截成功了,ViewB没有接受到此次事件。
现在我们来看下ViewGroupA输出的顺序:
1.dispatchTouchEvent
2.onInterceptTouchEvent
3.onTouchEvent
我们可以猜测为:事件传递给ViewGroupA,如果ViewGroupA不传递给子View(ViewB),说明那么他就会走onTouchEvent,即为自己处理。

现在我们看下,如果我们拦截后不处理当前事件会怎样:

public class ViewGroupA extends RelativeLayout {
...
/**
     * 拦截事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d("touch", "ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_DOWN    ");
                return true;
            case MotionEvent.ACTION_UP:
                Log.d("touch", "ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_UP ");
                return false;
            case MotionEvent.ACTION_CANCEL:
                Log.d("touch", "ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_CANCEL ");
                return false;
            default:
                return false;
        }
    }

}

输出日志:

D/touch: ViewGroupA :   dispatchTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewGroupA :   onTouchEvent   MotionEvent.ACTION_DOWN 
D/touch: TouchDispatchActivity onTouchEvent: MotionEvent.ACTION_DOWN
D/touch: TouchDispatchActivity onTouchEvent: MotionEvent.ACTION_UP

如果我们拦截后,处理了又怎样,将ViewGroupA中的onTouchEvent的Down事件返回为true:

 /**
     * 处理事件
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d("touch", "ViewGroupA :   onTouchEvent   MotionEvent.ACTION_DOWN ");
                return true;
            case MotionEvent.ACTION_UP:
                Log.d("touch", "ViewGroupA :   onTouchEvent   MotionEvent.ACTION_UP ");
                break;
            case MotionEvent.ACTION_CANCEL:
                Log.d("touch", "ViewGroupA :   onTouchEvent    MotionEvent.ACTION_CANCEL ");
                break;
        }
        return super.onTouchEvent(event);
    }

显示日志:

D/touch: ViewGroupA :   dispatchTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewGroupA :   onTouchEvent   MotionEvent.ACTION_DOWN 
D/touch: ViewGroupA :   dispatchTouchEvent    MotionEvent.ACTION_UP 
D/touch: ViewGroupA :   onTouchEvent   MotionEvent.ACTION_UP 
D/touch: TouchDispatchActivity onTouchEvent: MotionEvent.ACTION_UP

综合上面两个输出可以得出结论:
1.如果一个View拦截了,那么这个事件接下来的系列都会由他处理,并且不会调用onInterceptTouchEvent()。

2.如果一个View拦截,那么如果他不处理,那么这个事件接下来的系列就会交由他的父类来处理,即由其父类的onTouchEvent()来处理。

现在在ViewGroupA里不拦截,在ViewB中只处理Down

public class ViewB extends View {
...
/**
     * 事件处理
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d("touch", "ViewB onTouchEvent:  ACTION_DOWN ");
                return true;
            case MotionEvent.ACTION_UP:
                Log.d("touch", "ViewB onTouchEvent: ACTION_UP ");
                return false;
            case MotionEvent.ACTION_CANCEL:
                Log.d("touch", "ViewB onTouchEvent: ACTION_CANCEL ");
                return false;
            default:
                return false;
        }
    }
}

日志输出:

D/touch: ViewGroupA :   dispatchTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewB dispatchTouchEvent:  ACTION_DOWN 
D/touch: ViewB onTouchEvent:  ACTION_DOWN 
D/touch: ViewGroupA :   dispatchTouchEvent    MotionEvent.ACTION_UP 
D/touch: ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_UP 
D/touch: ViewB dispatchTouchEvent: ACTION_UP 
D/touch: ViewB onTouchEvent: ACTION_UP 
D/touch: TouchDispatchActivity onTouchEvent: MotionEvent.ACTION_UP

3.如果View不消耗除Down之外的其实事件,那么这个点击事件会消失,此时父View(VIewGroupA)的onTouchEvent()不会调用,并且当前的VIew可接收到持续事件,最终这些小时的点击事件会传给Activity处理

我们现在不拦截Down,只拦截UP看看会怎样:

public class ViewGroupA extends RelativeLayout {
...
 /**
     * 拦截事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d("touch", "ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_DOWN    ");
                return false;
            case MotionEvent.ACTION_UP:
                Log.d("touch", "ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_UP ");
                return true;
            case MotionEvent.ACTION_CANCEL:
                Log.d("touch", "ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_CANCEL ");
                return false;
            default:
                return false;
        }
    }

}

日志输出:

D/touch: ViewGroupA :   dispatchTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewB dispatchTouchEvent:  ACTION_DOWN 
D/touch: ViewB onTouchEvent:  ACTION_DOWN 
D/touch: ViewGroupA :   dispatchTouchEvent    MotionEvent.ACTION_UP 
D/touch: ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_UP 
D/touch: ViewB dispatchTouchEvent: ACTION_CANCEL 
D/touch: ViewB onTouchEvent: ACTION_CANCEL 
D/touch: TouchDispatchActivity onTouchEvent: MotionEvent.ACTION_UP

我们发现传递了down事件,拦截up事件的话,子View就会走ACTION_CANCEL。

你可能感兴趣的:(View的事件传递)