标签:
说到android事件的分发机制,真的是感觉既熟悉又陌生,因为每次需要用到的时候查看相关的源码,总能找到一些所以然来,但是要根据自己理解从头到尾说一遍,却一点都说不上。总结原因吧,感觉是自己不善于总结,过目就忘,并没有把心思放在上面,自然也就没有一点概念咯~~所以在这里主要是把自己理解的一些东西记录下来,不涉及源代码。
好吧,接下来简单说说android事件分发流程吧,说到事件分发,首先应该想到的是两个类,View和ViewGroup,ViewGroup是继承自View实现的,ViewGroup是控件容器,主要作用是包含具体控件,可以把ViewGroup想象成为一个盒子,里面按规则包含各种各样的控件,而包含控件的ViewGroup又可以把它当一个基本的控件单元,包含在另外一个ViewGroup中。实际上一个触摸事件是由上层的父类传递进来的,而最基本的ViewGroup的触摸事件则是由Activity传入的,Activity也有dispathTouchEvent()和onTouchEvent()方法,Activity接收到触摸事件时,会调用内部ViewGroup的dispathTouchEvent()方法。
事件分发流程和View相关的方法主要有两个:dispathTouchEvent()和onTouchEvent(), dispathTouchEvent()是分发事件的意思,onTouchEvent()才是真正处理事件的地方,实际上在View的dispathTouchEvent()方法中是通过调用onTouchEvent()处理事件的。在这里和触摸相关的还有一个onTouch方法,这个是使用控件的setOnTouchListene()设置的,它是满足一定条件时在onTouchEvent()方法之前调用的。
ViewGroup相关的方法除了dispathTouchEvent()和onTouchEvent(),还有一个onInterceptTouchEvent()方法,它表示的是拦截事件的意思,默认返回值是false,表示不拦截事件。另外一个就是onTouch方法了,这个也是使用控件的setOnTouchListene()设置的。
Activity_main.xml
<?xmlversion="1.0"encoding="utf-8"?> <com.example.test.testtouchevent.MyViewGroup xmlns:android="http://schemas.android.com/apk/res/android" android:tag="viewgroup1" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.example.test.testtouchevent.MyView android:tag="view1" android:layout_width="match_parent" android:layout_height="100dp" android:layout_margin="10dp" android:gravity="center" android:background="#ffff0000" android:text="view1" /> <com.example.test.testtouchevent.MyViewGroup android:id="@+id/mvg_v2" android:tag="viewgroup2" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:paddingBottom="50dp" android:background="#ff00ff00" android:orientation="vertical"> <com.example.test.testtouchevent.MyView android:id="@+id/mv_v2" android:tag="view2" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:layout_margin="10dp" android:gravity="center" android:background="#ffff0000" android:text="view2" /> <com.example.test.testtouchevent.MyView android:tag="view3" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:layout_margin="10dp" android:gravity="center" android:background="#ffff0000" android:text="view3" /> <com.example.test.testtouchevent.MyView android:tag="view4" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:layout_margin="10dp" android:gravity="center" android:background="#ffff0000" android:text="view4" /> </com.example.test.testtouchevent.MyViewGroup> <com.example.test.testtouchevent.MyView android:tag="view5" android:layout_width="match_parent" android:layout_height="100dp" android:layout_margin="10dp" android:gravity="center" android:background="#ffff0000" android:text="view5" /> </com.example.test.testtouchevent.MyViewGroup>
MainActivity.java
public class MainActivity extends Activity { private MyViewGroup mvgV2 = null; private MyView mvV2 = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(Config.show_onTouch) { mvgV2 = (MyViewGroup) findViewById(R.id.mvg_v2); mvV2 = (MyView) findViewById(R.id.mv_v2); mvgV2.setOnTouchListener(new MyTouchListener()); mvgV2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); mvV2.setOnTouchListener(new MyTouchListener()); mvV2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); } } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log("MainActivity" + ": " + "dispatchTouchEvent"); } // return super.dispatchTouchEvent(ev); if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log("MainActivity" + ": " + "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log("MainActivity" + ": " + "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log("MainActivity" + ": " + "dispatchTouchEvent ACTION_UP"); break; } } boolean flag = super.dispatchTouchEvent(ev); if(Config.show_default) { MyLog.log("MainActivity dispatchTouchEventreturn" + ": " + flag); } return flag; } @Override public boolean onTouchEvent(MotionEvent event) { if(Config.show_main) { MyLog.log("MainActivity" + ": " + "onTouchEvent"); } // return super.onTouchEvent(event); if(Config.detail) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log("MainActivity" + ": " + "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log("MainActivity" + ": " + "onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log("MainActivity" + ": " + "onTouchEvent ACTION_UP"); break; } } boolean flag = super.onTouchEvent(event); if(Config.show_default) { MyLog.log("MainActivity onTouchEventreturn" + ": " + flag); } return flag; } private class MyTouchListenerimplements View.OnTouchListener { @Override public boolean onTouch(View v, MotionEvent event) { if(v.getId() == R.id.mvg_v2) { if(Config.show_main) { MyLog.log(v.getTag().toString()+ ": " + "onTouch"); } if(Config.detail) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(v.getTag().toString()+ ": " + "onTouchACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_UP"); break; } } return false; } else if(v.getId() == R.id.mv_v2) { if(Config.show_main) { MyLog.log(v.getTag().toString()+ ": " + "onTouch"); } if(Config.detail) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_UP"); break; } } return false; } return false; } } }
MyView.java
public class MyView extends TextView { public MyView(Contextcontext) { super(context); } public MyView(Contextcontext, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent"); } if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP"); break; } } boolean flag = super.dispatchTouchEvent(ev); if(Config.show_default) { MyLog.log(getTag().toString()+ "dispatchTouchEvent return: " + flag); } return flag; } @Override public boolean onTouchEvent(MotionEvent event) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "onTouchEvent"); } // return super.onTouchEvent(event); if(Config.detail) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_UP"); break; } } boolean flag = super.onTouchEvent(event); if(Config.show_default) { MyLog.log(getTag().toString()+ "onTouchEvent return: " + flag); } return flag; } }
MyViewGroup.java
public class MyViewGroup extends LinearLayout { public MyViewGroup(Contextcontext, AttributeSet attrs) { super(context, attrs); } public MyViewGroup(Contextcontext) { super(context); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent"); } if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP"); break; } } boolean flag = super.dispatchTouchEvent(ev); if(Config.show_default) { MyLog.log(getTag().toString()+ "dispatchTouchEvent return: " + flag); } return flag; // return false; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent"); } // return true; if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent ACTION_UP"); break; } } boolean flag = super.onInterceptTouchEvent(ev); if(Config.show_default) { MyLog.log(getTag().toString()+ " onInterceptTouchEventreturn: " + flag); } return flag; } @Override public boolean onTouchEvent(MotionEvent event) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "onTouchEvent"); } if(Config.detail) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_UP"); break; } } boolean flag = super.onTouchEvent(event); if(Config.show_default) { MyLog.log(getTag().toString()+ "onTouchEvent return: " + flag); } return flag; } }
测试默认情况下时间分发流程,把Config.java的修改如下:
public staticfinal boolean show_onTouch= false; // 显示onTouch方法内容 public staticfinal boolean detail = false; // 显示详细的action内容 public staticfinal boolean show_main= true; // 显示主要内容 public staticfinal boolean show_default= true; // 显示默认返回值
1. 保持返回值为默认值,分别设置如下:
MainActivity:
dispathTouchEvent():super.dispathTouchEvent()
onTouchEvent():super. onTouchEvent()
MyViewGroup:
dispathTouchEvent():super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouchEvent():super.onTouchEvent()
MyView:
dispathTouchEvent():super.dispathTouchEvent()
onTouchEvent():super.onTouchEvent()
其他地方不做修改,运行程序。
见下图:
由此可见,触摸事件最先传递到MainActivity,使用dispathTouchEvent()进行分发,内部调用了viewgroup1的dispathTouchEvent(),之后调用onInterceptTouchEvent()判断是否拦截事件,这里返回false,紧接着调用viewgroup2的dispathTouchEvent(),通过遍历viewgroup2的子控件,最后找到了view2控件,调用view2的dispatchTouchEvent()方法,内部调用了onTouchEvent()方法,所有的方法都返回了false,所以又继续返回父控件,最后到MainActivity的onTouchEvent()。为什么是这样的流程需要看下源码,里面可以很清晰了解具体的流程,这里只想简单说明事件分发的流程。
1.默认情况下
dispatchTouchEvent(),onInterceptTouchEvent(),onTouchEvent()返回值都是false,表示不消费touch事件。
2.view的事件分发过程:dispatchTouchEvent()->onTouchEvent()
3.viewgroup事件分发过程:
dispatchTouchEvent()->onInterceptTouchEvent()->onTouchEvent()
上述结果都是在各个处理流程返回默认值的时候成立,也就是没有控件消费事件的时候。
在activity_main.xml中对viewgroup2和view2设置,加入如下代码:
android:clickable="true"
把Config.java的修改如下:
public staticfinal boolean show_onTouch= true; // 显示onTouch方法内容 public staticfinal boolean detail = false; // 显示详细的action内容 public staticfinal boolean show_main= true; // 显示主要内容 public staticfinal boolean show_default= false; // 显示默认返回值
各方法返回值:
MainActivity:
dispathTouchEvent():super.dispathTouchEvent()
onTouchEvent():super. onTouchEvent()
MyViewGroup:
dispathTouchEvent(): super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent ()
onTouch(): false
onTouchEvent():super.onTouchEvent()
MyView:
dispathTouchEvent():super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch():false
onTouchEvent():super.onTouchEvent()
由图可见,view2的onTouch已经触发了,它是在dispatchTouchEvent()和onTouchEvent()之间调用的,调用顺序是:
dispatchTouchEvent()->onTouch()->onTouchEvent()。
另外,由于设置了view2的clickable属性,它的onTouchEvent()是返回true的。所以viewgroup2的onTouch()并没有触发,需要把MyView的onTouchEvent()返回值设置成false才会触发。这里就不贴测试结果了,viewgroup2的调用顺序是这样的:
dispatchTouchEvent()->onInterceptTouchEvent()->onTouch()->onTouchEvent(),结合View和ViewGroup的例子说明,onTouch()是在onTouchEvent()之前调用的。
因为View的onTouchEvent()返回true,表示已经消费了改事件,所以事件不会向上投递了,后面的ViewGroup的onTouchEvent()也不会调用。
1.View调用顺序是:dispatchTouchEvent()->onTouch()->onTouchEvent()。
2.ViewGroup调用顺序:
dispatchTouchEvent()->onInterceptTouchEvent()->onTouch()->onTouchEvent()
3.如果设置了View的clickable属性,那么onTouchEvent()返回true,代表消费该事件,后续将不会继续投递该事件。
单纯把MyView的onTouchEvent()的返回值设置成false,其他条件和测试2保持不变,代码如下:
@Override public boolean onTouchEvent(MotionEvent event) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "onTouchEvent"); } // return super.onTouchEvent(event); if(Config.detail) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_UP"); break; } } boolean flag = super.onTouchEvent(event); if(Config.show_default) { MyLog.log(getTag().toString()+ "onTouchEvent return: " + flag); } return false; }
可以看到,当view2的onTouchEvent()返回false时,表示事件没有被消费,所以继续向上投递,viewgroup2的onTouch()和onTouchEvent()都会触发。因为viewgroup2设置了clickable属性,所以它的onTouchEvent()是返回true的,所以事件就不会继续向上投递了,如果把ViewGroup的onTouchEvent()返回值手动改成false,那么viewgroup1的onTouchEvent()还是会触发的。
1.当一个View设置了clickable为true属性之后,他的onTouchEvent()会返回true,表示消费了touch事件。
设置返回值:
MainActivity:
dispathTouchEvent():super.dispathTouchEvent()
onTouchEvent():super. onTouchEvent()
MyViewGroup:
dispathTouchEvent():super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch():false
onTouchEvent():super.onTouchEvent()
MyView:
dispathTouchEvent():super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch():true
onTouchEvent():super.onTouchEvent()
其他条件和测试3一样
可以看到把view2的onTouch返回true,则接下来的onTouchEvent()也不会调用,因为这样设置代表view2已经处理了事件,那么之后onTouchEvent()也不会执行了。
1.当onTouch ()返回true之后,后续的onTouchEvent()不会触发
把Config.java的修改如下:
public staticfinal boolean show_onTouch= false; // 显示onTouch方法内容 public staticfinal boolean detail = true; // 显示详细的action内容 public staticfinal boolean show_main= false; // 显示主要内容 public staticfinal boolean show_default= false; // 显示默认返回值
设置如下返回值
MainActivity:
dispathTouchEvent(): super.dispathTouchEvent()
onTouchEvent(): super.onTouchEvent()
MyViewGroup:
dispathTouchEvent():true
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch(): false
onTouchEvent(): super.onTouchEvent()
MyView:
dispathTouchEvent(): super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch(): false
onTouchEvent(): super.onTouchEvent()
ViewGroup的dispathTouchEvent()代码如下:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent"); } if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP"); break; } } // boolean flag =super.dispatchTouchEvent(ev); // // if(Config.show_default) { // MyLog.log(getTag().toString() +" dispatchTouchEvent return: " + flag); // } // // return flag; return true; }
当viewgroup1的dispathcTouchEvent()返回true之后,代表dispathcTouchEvent()消费了改事件,那么事件不会继续往下投递,所以后面的viewgroup2和view2都不会接受到此事件,另外一个对于viewgroup1来说,接下来的onTouchEvent()也不会执行,因为事件已经被消费掉了。
1.ViewGroup的dispathcTouchEvent()返回true,代表关注该事件,后续的touch事件由它处理。
在测试5的基础上修改
MainActivity:
dispathTouchEvent(): super.dispathTouchEvent()
onTouchEvent(): super.onTouchEvent()
MyViewGroup:
dispathTouchEvent():false
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch(): super.onTouch()
onTouchEvent(): super.onTouchEvent()
MyView:
dispathTouchEvent(): super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch(): super.onTouch()
onTouchEvent(): super.onTouchEvent()
当把ViewGroup的dispathTouchEvent()设置为false,表示当前的ViewGroup并不关心该事件,那么下次事件的ACTION_MOVE和ACTION_UP它也不会处理,直接交给了MainActivity进行处理,但是当ACTION_UP触发之后,表示一个完整事件已经完成,那么当ACTION_DOWN再次触发的时候,事件还是会投递到ViewGroup,当ViewGroup的dispathTouchEvent()返回false,表示不关系此事件,那么后续的ACTION_MOVE和ACTION_UP会直接分发给MainActivity进行处理。
1. 一个完成的触摸事件一般包括ACTION_DOWN,ACTION_MOVE,ACTION_UP等,ACTION_MOVE,ACTION_UP在一个触摸事件中不一定会出现。
2. ViewGroup的dispathTouchEvent()返回false,之后的action不会继续投递到该ViewGroup进行处理。这些action包括ACTION_MOVE,ACTION_UP等,直到下一次ACTION_DOWN事件出现
设置如下返回值
MainActivity:
dispathTouchEvent(): super.dispathTouchEvent()
onTouchEvent(): super.onTouchEvent()
MyViewGroup:
dispathTouchEvent():uper.dispathTouchEvent()
onInterceptTouchEvent():false
onTouch(): super.onTouch()
onTouchEvent(): super.onTouchEvent()
MyView:
dispathTouchEvent(): super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch(): super.onTouch()
onTouchEvent(): super.onTouchEvent()
修改ViewGroup的代码如下:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent"); } if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP"); break; } } boolean flag = super.dispatchTouchEvent(ev); if(Config.show_default) { MyLog.log(getTag().toString()+ "dispatchTouchEvent return: " + flag); } return flag; // return false; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent"); } // return true; if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_MOVE"); return true; // break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent ACTION_UP"); break; } } // boolean flag =super.onInterceptTouchEvent(ev); // if(Config.show_default) { // MyLog.log(getTag().toString() +" onInterceptTouchEvent return: " + flag); // } return false; }
流程和测试1保持默认值是一样的。
在测试7的条件下设置如下:
MainActivity:
dispathTouchEvent(): super.dispathTouchEvent()
onTouchEvent(): super.onTouchEvent()
MyViewGroup:
dispathTouchEvent():uper.dispathTouchEvent()
onInterceptTouchEvent():true
onTouch(): super.onTouch()
onTouchEvent():true
MyView:
dispathTouchEvent(): super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch(): super.onTouch()
onTouchEvent(): super.onTouchEvent()
由于viewgroup1的onInterceptTouchEvent()返回true,表示截取该事件,所以事件不会继续向下投递,直接有viewgroup1的onTouchEvent()进行处理,如果viewgroup1的
onTouchEvent()返回true,那么下次触摸事件还会继续进入到viewgroup1,由viewgroup1进行处理。
1. onInterceptTouchEvent()方法表示是否事件,默认返回值是false,表示不拦截事件。
2. onInterceptTouchEvent()返回true表示拦截事件,事件不会继续向下投递,直接交给onTouchEvent()处理。
ViewGroup的onInterceptTouchEvent()返回true之后,表示事件被拦截,那么事件将不会继续分发下去,有没有办法设置父类不拦截事件呢,ViewGroup给我们提供了一个方法:
requestDisallowInterceptTouchEvent()方法,如果我们想父类不拦截touch事件,那么只需要设置成requestDisallowInterceptTouchEvent(true)即可。
requestDisallowInterceptTouchEvent()这个方法不能在ViewGrop的dispathTouchEvent()里面设置,因为ViewGroup在ACTION_DOWN时会把FLAG_DISALLOW_INTERCEPT设置为false,相当于之前设置的requestDisallowInterceptTouchEvent()并没有起作用,这个时候如果事件被拦截,那么事件将不会继续分发到子View,事件还是会被拦截,相当于requestDisallowInterceptTouchEvent()设置并没有起作用。
基于上述测试8代码修改,把MyViewGroup修改为:
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent"); } // return true; if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_MOVE"); return true; // break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent ACTION_UP"); break; } } // boolean flag =super.onInterceptTouchEvent(ev); // if(Config.show_default) { // MyLog.log(getTag().toString() +" onInterceptTouchEvent return: " + flag); // } return false; }
这里只是在ACTION_MOVE截取了,子类不会接收到这个touch事件。
由于在ViewGroup代码中设置了onInterceptTouchEvent()的ACTION_MOVE返回true,所以ACTION_MOVE后面的action子View都不会接受到,本次ACTION_MOVE就直接由ViewGroup的onTouchEvent()进行消费了,我们在onTouchEvent()返回true,那么这个touch事件的其他action以后都由这个ViewGroup进行处理。onInterceptTouchEvent()在之后的action都不会触发。
onInterceptTouchEvent()返回true会截取该事件,由截取该事件的View进行处理,而且onInterceptTouchEvent()在下次ACTION_DOWN来临之前都不会重复调用。
在上述测试的基础上修改MyView代码:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString() + ": " + "dispatchTouchEvent"); } if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN"); getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP"); break; } } boolean flag = super.dispatchTouchEvent(ev); if(Config.show_default) { MyLog.log(getTag().toString() + " dispatchTouchEvent return: "+ flag); } return flag; }
和上述测试结果对比可以指,ViewGroup对ACTION_MOVE的处理还是会分发到子View,这是由于子View调用了getParent().requestDisallowInterceptTouchEvent(true);告诉父类不要截取事件,所以之后的action还是会分发到子View上。
requestDisallowInterceptTouchEvent(true)可以让父控件不截取事件。
onTouch()触发条件
添加onTouch()方法,在activity_main.xml中对viewgroup2和view2设置,加入如下代码:
android:clickable="true"
但是我加入了之后还是不会触发onTouch(),查看了源码之后才发现新版本和旧版本原来存在差异,在新版本中代码是这样的:
ListenerInfoli = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; }
而这个ListenerInfo的初始化可以在setOnClickListener()设置,当然其他地方会有设置。
public void setOnClickListener(@Nullable OnClickListener l) { if (!isClickable()) { setClickable(true); } getListenerInfo().mOnClickListener = l; }
所以可以给这个控件设置一个点击监听器,就可以出发onTouch()方法了。
在MainActivity中加入如下代码:
mvgV2 = (MyViewGroup) findViewById(R.id.mvg_v2); mvV2 = (MyView) findViewById(R.id.mv_v2); mvgV2.setOnTouchListener(new MyTouchListener()); mvgV2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); mvV2.setOnTouchListener(new MyTouchListener()); mvV2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } });
1. 默认情况下dispatchTouchEvent(),onInterceptTouchEvent(),onTouchEvent()返回值都是false,表示不消费touch事件。
2.view的事件分发过程:dispatchTouchEvent()->onTouchEvent()
3.viewgroup事件分发过程:
dispatchTouchEvent()->onInterceptTouchEvent()->onTouchEvent()
4.对于View和ViewGroup来说,onTouch()的调用在onTouchEvent()之前。
5. 如果设置了View的clickable属性,那么onTouchEvent()返回true
6.返回值true表示关注此事件,如果一个View返回true,后续的事件都会分发给该View处理。
7.如果一个View返回false,表示不关注此事件,那么该事件后续的action比如ACTION_MOVE,ACTION_UP都不会分发给该View。
8.onInterceptTouchEvent()用来判断是否截取事件,默认不截取,如果返回true,表示截取该事件,那么之后的touch事件直接交由该View的onTouchEvent()进行处理。
9.可以通过requestDisallowInterceptTouchEvent()设置父类是否截取事件,传入true表示不截取事件。
10.requestDisallowInterceptTouchEvent()不能在ViewGroup的dispathTouchEvent中设置,因为ACTION_DOWN到来是会重新把FLAG_DISALLOW_INTERCEP设置成false。
最后附上一张流程图:
标签:
原文地址:http://blog.csdn.net/qq282330332/article/details/51887436