view事件机制

想要实现绚丽的交互效果,自定义view是必不可少的,下面就讲讲自定义view中很重要的一点——view事件机制

参与view事件的三个方法:
public boolean dispatchTouchEvent(MotionEvent ev);
public boolean onInterceptTouchEvent(MotionEvent ev) ;
public boolean onTouchEvent(MotionEvent event) ;
事件分发,事件拦截,onTouchEvent三个方法的返回值都是boolean,这三个方法意义是什么,他们的返回值又表示什么,下面通过一个案例来进行说明。

自定义一个viewgruop和一个view
viewgroup包含一个子view
viewgroup代码如下:

public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i("TAG", "root dispatchTouchEvent start "+"action="+ev.getAction());
        boolean b= super.dispatchTouchEvent(ev);
        Log.i("TAG", "root dispatchTouchEvent "+b+"action="+ev.getAction());
        return b;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i("TAG", "root onInterceptTouchEvent start "+"action="+ev.getAction());
        boolean b=super.onInterceptTouchEvent(ev);
        Log.i("TAG", "root onInterceptTouchEvent "+b+"action="+ev.getAction());
        return b;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("TAG", "root onTouchEvent start "+"action="+event.getAction());
        boolean b=super.onTouchEvent(event);
        Log.i("TAG", "root onTouchEvent "+b+"action="+event.getAction());
        return b;
    }

自定义view代码如下

public boolean dispatchTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        Log.i("TAG", "view dispatchTouchEvent start "+"action="+event.getAction());
        boolean b=super.dispatchTouchEvent(event);
        Log.i("TAG", "view dispatchTouchEvent "+b+"action="+event.getAction());
        return b;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("TAG", "view onTouchEvent start "+"action="+event.getAction());
        boolean b= super.onTouchEvent(event);
        Log.i("TAG", "view onTouchEvent "+b+"action="+event.getAction());
        return b;
    }

a,在不做任何改动的情况下运行app
查看log输出信息如图
这里写图片描述
通过log信息可以看出事件到来的时候会先到viewgroup的事件分发,事件分发方法拿到事件后执行事件拦截,这里事件拦截返回false,执行子view的事件分发,子view没有事件拦截(view处于事件末端)。在子view事件分发中执行子view的onTouchEvent,子view的onTouchEvent返回true,子view的事件分发拿到onTouchEvent的结果,返回true给viewgroup的事件分发,viewgroup事件分发返回true。
也许从这里看不出什么眉目,下面再往下看。

b,修改viewgroup的返回值为true
查看log输出信息如图
这里写图片描述
在viewgroup事件分发拿到事件后,执行viewgroup的事件拦截,viewgroup事件拦截返回true,没有把事件传递给view而是直接执行viewgroup的onTouchEvent,viewgroup的onTouchEvent返回为false,viewgroup事件分发返回false,然后就没了(没有action_up)的时间传递过来,对比默认的log和修改viewgroup的返回值为true的log,我们可以看出事件拦截的作用
1,事件拦截返回false,表示当前事件不需要拦截,可以把事件给子view处理
2,事件拦截返回true,表示当前事件需要拦截,不会把事件给子view,直接执行自身的onTouchEvent;

现在我们却有了一个新的问题,为什么这里只有action_dowm的事件而没有action_up的事件,不要急,继续往下看,一切都会揭晓.

c,修改viewgroup的返回值为true同时修改viewgroup的onTouchEvent返回为true
查看log输出信息如图
这里写图片描述

这里和上面都是事件拦截返回true,但不同的是这里viewgroup收到了action_up的事件,我们来看看是哪里不同导致viewgroup没有收到action_up事件。
在修改viewgroup的返回值为true的时候,viewgroup的onTouchEvent返回了false,viewgroup的事件分发也返回false,就没有结果了。但是如果让 viewgroup的onTouchEvent返回true,会导致viewgroup事件分发返回true,viewgroup才能收到action_down后面的事件。
在这里大致可以推断出事件分发的功能:
*1,执行事件拦截,看当前viewgroup需不需要拦截当前事件,如果需要(返回true),直接执行viewgroup的onTouchEvent,如果不需要,把事件给子view处理。
2,返回为true表示对当前的一系列事件感兴趣(需要action_dowm后面的action_move和action_up),可以收到后面的事件;返回false表示对当前一系列事件没兴趣,那么后面的事件不会传递过来。*

那么事件分发的返回值受什么因素的影响呢?通过bc我们可以发现事件分发返回值受当前view的onTouchEvent的影响,即他们是相关的,但是在a中没有执行viewgroup的onTouchEvent,事件分发返回却是true。a和bc不同的是a把事件传递给了子view,现在我们来修改下子view的事件分发返回值,看看子view的事件分发返回结果和viewgroup事件分发返回结果有什么关联

view事件分发返回false
查看log输出信息如图
这里写图片描述

在这里,viewgroup没有对事件进行拦截,事件传递给了子view,由于子view的onTouchEvent返回了false,导致子view的事件分发返回false,viewgroup收到子view事件分发返回false后执行自身的onTouchEvent,onTouchEvent返回true,viewgroup的事件分发返回true,表示对该系列事件感兴趣。当viewgroup收到后续事件的时候,没有执行事件拦截(拦截的目的是为了直接执行viewgroup的onTouchEvent,现在子view表示对该系列事件没兴趣,事件不会给它,就没必要去拦截),直接执行viewgroup的onTouchEvent,onTouchEvent执行完后把结果给了viewgroup的事件分发,事件分发在把结果返回给上级。
这里可以看出
事件分发结果是要返还给上级的,如果子view的事件分发返回了false(表示子view不处理该事件),会执行viewgroup的onTouchEvent,在此处事件分发最终结果受onTouchEvent影响

在首次事件到来的时候事件流程如下
这里写图片描述

事件流程文字描述:
1,事件分发第一时间拿到事件,拿到事件后判断事件是否需要拦截;
2.1,如果需要拦截,直接执行onTouchEvent,onTouchEvent的返回结果就是事件分发的返回结果;
2.2,如果不需要拦截,把事件传递给子view,并拿到子view事件分发返回结果
2.2.1子view事件分发返回结果为true,事件分发直接返回true;
2.2.2子view事件分发返回false,执行自身onTouchEvent,onTouchEvent的返回结果就是事件分发的返回结果;

用伪代码表示如下

public class ViewEvent {
    private boolean isInterceptTouchEvent=false;
    private boolean isChildHandleEvent=false;
    private ViewEvent child;
    /**
     * 事件分发伪代码
     * @param ev
     * @return
     */
    public boolean dispatchTouchEvent(MotionEvent ev){
        boolean result=false;
        //isInterceptTouchEvent表示事件是否已经拦截
        if(isInterceptTouchEvent){//viewgroup已经拦截了事件,不用执行后面的
            result=onTouchEvent(ev);//事件交由viewgroup的onTouchEvent处理
            return result;
        }
        //如果子view没有处理事件,表示view对后续事件不感兴趣
        //不会让子view处理该事件
        if(isChildHandleEvent==false){
            result=onTouchEvent(ev);//事件交由viewgroup的onTouchEvent处理
            return result;
        }
        if(onInterceptTouchEvent(ev)){//如果当前viewGroup拦截事件
            result=onTouchEvent(ev);//事件交由viewgroup的onTouchEvent处理
        }else{//viewgroup没有拦截当前事件 交给子view处理
             result=child.dispatchTouchEvent(ev);
             if(result==false){//如果子view没有消费该事件
                 isChildHandleEvent=false;//记录
                 result=onTouchEvent(ev);//调用自身的onTouchEvent去消费事件
             }
        }
        return result;
    }

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

}

这里给出一些结论,如果读者感兴趣可以尝试log确认。
1,viewgroup第一次拦截事件后,第二次事件到来不会在判断是否需要拦截,而是直接执行onTouchEvent;
2,事件分发返回false,后续事件不会再给此view;
3,如果viewgroup的所有子view事件分发返回都为false,下次事件到来时会直接执行onTouchevent;
4,即使viewgroup拦截了事件,如果viewgroup上级拦截事件,viewgroup将不会收到后续事件(listview item侧滑的时候突然向上滑动,上滑事件会被listview拦截)。

如果有问题请大家指出,我会及时更正.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值