android View 事件分发

关于事件分发,其实看了很多资料,看的时候都是看得懂,但是实际运用于事件冲突的时候,却不知道如何下手,希望能写完这篇文章跟事件冲突的文章后,能有所进步。

先上代码 ,很实用的伪代码

//伪代码
public void dispatchTouchEvent(MotionEvent ev){
    boolean consume = false ;//用这个变量 实现单通道出口
    if(interceptTouchEvent(ev)){//拦截方法 默认是不拦截的
        consume = onTouchEvent(ev);
    }else{
        consume = child.dispatchTouchEvent(ev);//父类如果不拦截 就会将事件传递到子类去,这样一直传下去 ,直到子类处理 返回true  ,如果一直不处理即返回false ,那又会逐级上传
    }
    return consume ; //如果为true 表示这个事件被当前的view所消费了 
}

MotionEvent 点击事件对象 三个重点方法 dispatchTouchEvent() onInterceptTouchEvent() onTouchEvent()

dispatchTouchEvent(): 用于传递事件 ,如果一个view能接收到事件,那它的这个方法必定会被触发,可以理解为这是view的事件入口,返回值代表着是否消耗了这个事件。
onInterceptTouchEvent():用于拦截事件,只有viewgroup中才有这个方法,另外 如果这个view拦截了这个事件,那么在接下来的事件当中,他不会被触发,拦截后消费掉这个事件,那之后的方法也会直接跳到这个view头上,然而拦截后却不消费掉这个方法(即onTuchEvent()方法返回false),那么当前的view是无法再接收到事件了,之后的事件会跳转到父view去。 具体的内容,可以看代码。
onTouchEvent(): 用来处理点击事件的,只要事件被拦截 或者本身是view,那么就会触发这个事件,如果返回true ,就是真正的消费了事件,一般dispatchTouchEvent的返回值 就是这个方法的返回值。如果用户对view设置了onTouchListener ,那么会调用onTouch方法 ,这个方法的优先级会比onTouchEvent的方法高。如果onTouch返回true,表示事件被消费了,onTouchEvent直接不会被调用,当返回false的时候 后者才会被正常的调用起来。而onTouchEvent方法中会有判断是否设置了onClickListener ,设置了的话 onClick会被调用。
简单的来说 优先级 onTouch> onTouchEvent>onClick

点击事件的传递路径 :Activity —>Window ——>View 关于Window 跟DecorView 回头再慢慢说。 如果下面的对象都没有消费这个事件的话,又会逐级的上传,这个过程中,如果还是没有人消费这个事件,最终会调用Activity的onTouchEvent方法。

文中总结的结论 挑重点复述:

  1. 一个系列事件是以down事件开始,中间穿插着不定量的move事件,最后以up方法结尾
  2. 正常情况下,一个事件序列只能被一个view拦截,正常的情况他是拦截后,会把后续的事件都传给这个view处理,当然也可以在onTouchEvent方法中直接将方法强制传给其它view处理(有个疑问:父类子类都可以吗?)。
  3. View拦截事件后,他的onInterceptTouchEvent不会被重复调用,后续的事件会直接传递到他身上。
  4. 如果一个view要处理事件,那么从down方法开始 就必须返回true,否者后续的事件会被交给父类去处理。如果down方法返回true,后续的事件返回false,那么这个方法会直接消失,并不会交给父类去做,并且后续的事件依然存在,依然会传递过来,最后这些消失的事件会统一交给activity处理。
  5. Viewgroup默认不拦截事件,即onInterceptTouchEvent方法默认返回false,所以有需求的时候需要自己重写这个方法。
  6. View没有onInterceptTouchEvent方法。
  7. View的onTouchEvent这个方法默认返回true,除非他是不可点击的(clickable or longClickable都为false),前者根据控件确认值,而后者默认都是false。
  8. enable这个属性不会影响onTouchEvent的值,因为这个方法只会去判断clickable跟longClickable 是否为false,这两者只要有一个为true,这个方法就会被正常的进行,返回true。
  9. onClick发生的前提 是这个view是可点击的 ,并且他收到了down跟up方法,这其实也说明这个方法是在up之后才会被触发。(当你设置了onClickListener接口后,会自动就将clickable设置为true,)
  10. 事件传递都是由外向内的,都是从父元素慢慢的分发传递到子元素去的,但是子view中可以调用requestDisallowTouchEvent方法可以干预父元素的事件传递,但是down事件除外。(因为down事件的时候会直接重置这个request状态,所以即使你设置了, 也是无效的)。

如上所说的,一个事件最初是从activity开始的,他会在dispatchTouchEvent方法中调用他的内部Window,Window会将事件传递给DecorView,这个就是setContentView中的view的父容器,这个存在可以通过 Activity.getWindow().getDecorView()方法获取

//Activity中
public boolean dispatchTouchEvent(MotionEvent ev){
    if(ev.getAction()==MotionEvent.ACTION_DOWN ){
        onUseInteraction();//???
    }
    if(getWindow().superDispatchTouchEvent(ev)){//同上,调用window的方法 逐级传递 
        //如果条件成立 所以里面的存在把事件给消费了 ,那么就直接返回true
        return true ;
    }
    return onTouchEvent(ev);//子元素没有一个消费掉了这个方法,所以会调用activity的onTouchEvent
}
//PhoneWindow的superDispatchTouchEvent 
public boolean superDispatchTouchEvent (MotionEvent ev){
    return mDecor.superDispatchTouchEvent(ev);//直接调用DecorView的方法 根本没有做其它多余的操作 ,就是传递事件
}

关于window就不详说了,是个抽象类,他的实现类是PhoneWindow

我们之前在setContentView中设置的view ,除了自己用id来找以外,还可以通过这个mDecorView获取
((ViewGroup)getWindow().getDecorView().findViewById(android.R.id.content)).getChildAt(0); 不过这么写真的麻烦 ,只是说明一下而已。
DecorView继承FrameLayout ,另外 事件传递的过程中,除非事件被拦截,不然onTouchListener不起作用。
所以说 想做事件传递分发的时候,基本都是重写 Viewgroup的dispathchTouchEvent方法

//viewgroup的  dispatchTouchEvent的方法片段
final boolean intercepted ;
//为down 或者后者不为null  后者是否为null取决于是否有子元素处理了事件,如果有处理,那么这个对象就会指向子元素
if(actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget !=null){
    //判断子类有没使用requestDisallowTouchEvent方法把 猜猜应该是设置了之后返回的是false 那么父类就不拦截了
    final boolean disallowIntercept  = (mGroupFlags &FLAG_DISALLOW_INTERCEPT )!=0;
    //能作用除down以外的其它动作,如果是down的话 ,viewgroup还是会调用intercept方法判断是否拦截的
    if(!disallowIntecept){
        intecepted = onInterceptTouchEvent(ev);
        ev.setAction(action);//防止动作被改变 所以重置它
    }else{
        //不拦截 子类才能搞事情 是吗? 
        intercepted = false ;
    }
}else{
    //不是down事件  子view也没处理事件 那么就会直接跳到这里来 并没有经过onInterceptTouchEvent
    intercepted = false ;

}
//如上代码表明  onInterceptTouchEvent不是每次都会被调用的

这里写图片描述
view里面的方法 ,看的出来 没有intercept方法,并且onTouch方法优先级很高

这里写图片描述
看view的onTouchEvent方法中 ,看出 可用不可用 都不影响view消费掉这个事件,如果它不可用,他会判断他的clickable 跟longclickable方法 哪个为true 只要为true 就能玩 还有其它的一些代码也证明了这一点。
//关于setClickListener 跟 setLongClcikListener 只要设置了这个监听 ,那两个状态就自动会被修改。

好吧 还需要修改 需要再去多看看其它的文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值