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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,820评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,648评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,324评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,714评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,724评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,328评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,897评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,804评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,345评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,431评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,561评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,238评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,928评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,417评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,528评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,983评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,573评论 2 359

推荐阅读更多精彩内容