android事件分发流程

来源:互联网 发布:企业电话查询软件 编辑:程序博客网 时间:2024/06/04 18:11

1.  描述

说到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()设置的。

 

2.  测试界面:

     

3.  测试代码:

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;    } }


4.  测试例子:

4.1.  测试1

4.1.1.  测试条件:

测试默认情况下时间分发流程,把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()

其他地方不做修改,运行程序。

4.1.2.  输出结果:

 

4.1.3.  流程分析:

见下图:


       由此可见,触摸事件最先传递到MainActivity,使用dispathTouchEvent()进行分发,内部调用了viewgroup1的dispathTouchEvent(),之后调用onInterceptTouchEvent()判断是否拦截事件,这里返回false,紧接着调用viewgroup2的dispathTouchEvent(),通过遍历viewgroup2的子控件,最后找到了view2控件,调用view2的dispatchTouchEvent()方法,内部调用了onTouchEvent()方法,所有的方法都返回了false,所以又继续返回父控件,最后到MainActivity的onTouchEvent()。为什么是这样的流程需要看下源码,里面可以很清晰了解具体的流程,这里只想简单说明事件分发的流程。

 

4.1.4.  结论

1.默认情况下

dispatchTouchEvent(),onInterceptTouchEvent(),onTouchEvent()返回值都是false,表示不消费touch事件。

2.view的事件分发过程:dispatchTouchEvent()->onTouchEvent()

3.viewgroup事件分发过程:

dispatchTouchEvent()->onInterceptTouchEvent()->onTouchEvent()

上述结果都是在各个处理流程返回默认值的时候成立,也就是没有控件消费事件的时候。

 

4.2.  测试2

4.2.1.  测试条件

在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()

 

4.2.2.  输出结果


4.2.3.  流程分析

由图可见,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()也不会调用。

4.2.4.  结论

1.View调用顺序是:dispatchTouchEvent()->onTouch()->onTouchEvent()。

2.ViewGroup调用顺序:

dispatchTouchEvent()->onInterceptTouchEvent()->onTouch()->onTouchEvent()

3.如果设置了View的clickable属性,那么onTouchEvent()返回true,代表消费该事件,后续将不会继续投递该事件。

 

4.3.  测试3

4.3.1.  测试条件

单纯把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;    }

4.3.2.  输出结果


4.3.3.  流程分析

可以看到,当view2的onTouchEvent()返回false时,表示事件没有被消费,所以继续向上投递,viewgroup2的onTouch()和onTouchEvent()都会触发。因为viewgroup2设置了clickable属性,所以它的onTouchEvent()是返回true的,所以事件就不会继续向上投递了,如果把ViewGroup的onTouchEvent()返回值手动改成false,那么viewgroup1的onTouchEvent()还是会触发的。

4.3.4.  结论

1.当一个View设置了clickable为true属性之后,他的onTouchEvent()会返回true,表示消费了touch事件。

 

 

4.4.  测试4

4.4.1.  测试条件

设置返回值:

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一样


4.4.2.  输出结果


4.4.3.  流程分析

可以看到把view2的onTouch返回true,则接下来的onTouchEvent()也不会调用,因为这样设置代表view2已经处理了事件,那么之后onTouchEvent()也不会执行了。

4.4.4.  结论

1.当onTouch ()返回true之后,后续的onTouchEvent()不会触发

 

4.5.  测试5

4.5.1.  测试条件

把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;    }

4.5.2.  输出结果


4.5.3.  流程分析

当viewgroup1的dispathcTouchEvent()返回true之后,代表dispathcTouchEvent()消费了改事件,那么事件不会继续往下投递,所以后面的viewgroup2和view2都不会接受到此事件,另外一个对于viewgroup1来说,接下来的onTouchEvent()也不会执行,因为事件已经被消费掉了。

4.5.4.  结论

1.ViewGroup的dispathcTouchEvent()返回true,代表关注该事件,后续的touch事件由它处理。

 

4.6.  测试6

4.6.1.  测试条件

在测试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()

4.6.2.  输出结果


4.6.3.  流程分析

当把ViewGroup的dispathTouchEvent()设置为false,表示当前的ViewGroup并不关心该事件,那么下次事件的ACTION_MOVE和ACTION_UP它也不会处理,直接交给了MainActivity进行处理,但是当ACTION_UP触发之后,表示一个完整事件已经完成,那么当ACTION_DOWN再次触发的时候,事件还是会投递到ViewGroup,当ViewGroup的dispathTouchEvent()返回false,表示不关系此事件,那么后续的ACTION_MOVE和ACTION_UP会直接分发给MainActivity进行处理。

4.6.4.  结论

1.     一个完成的触摸事件一般包括ACTION_DOWN,ACTION_MOVE,ACTION_UP等,ACTION_MOVE,ACTION_UP在一个触摸事件中不一定会出现。

2.     ViewGroup的dispathTouchEvent()返回false,之后的action不会继续投递到该ViewGroup进行处理。这些action包括ACTION_MOVE,ACTION_UP等,直到下一次ACTION_DOWN事件出现


4.7.  测试7

4.7.1.  测试条件

设置如下返回值

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;    }


4.7.2.  输出结果

4.7.3.  流程分析

流程和测试1保持默认值是一样的。


4.8.  测试8

4.8.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()

4.8.2.  输出结果

4.8.3.  流程分析

由于viewgroup1的onInterceptTouchEvent()返回true,表示截取该事件,所以事件不会继续向下投递,直接有viewgroup1的onTouchEvent()进行处理,如果viewgroup1的

onTouchEvent()返回true,那么下次触摸事件还会继续进入到viewgroup1,由viewgroup1进行处理。

4.8.4.  结论

1.     onInterceptTouchEvent()方法表示是否事件,默认返回值是false,表示不拦截事件。

2.     onInterceptTouchEvent()返回true表示拦截事件,事件不会继续向下投递,直接交给onTouchEvent()处理。

 

5.  关于ViewGroup的事件拦截

ViewGroup的onInterceptTouchEvent()返回true之后,表示事件被拦截,那么事件将不会继续分发下去,有没有办法设置父类不拦截事件呢,ViewGroup给我们提供了一个方法:

requestDisallowInterceptTouchEvent()方法,如果我们想父类不拦截touch事件,那么只需要设置成requestDisallowInterceptTouchEvent(true)即可。

requestDisallowInterceptTouchEvent()这个方法不能在ViewGrop的dispathTouchEvent()里面设置,因为ViewGroup在ACTION_DOWN时会把FLAG_DISALLOW_INTERCEPT设置为false,相当于之前设置的requestDisallowInterceptTouchEvent()并没有起作用,这个时候如果事件被拦截,那么事件将不会继续分发到子View,事件还是会被拦截,相当于requestDisallowInterceptTouchEvent()设置并没有起作用。

5.1.  测试1

5.1.1.  测试条件

基于上述测试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事件。

5.1.2.  输出结果

5.1.3.  流程分析

由于在ViewGroup代码中设置了onInterceptTouchEvent()的ACTION_MOVE返回true,所以ACTION_MOVE后面的action子View都不会接受到,本次ACTION_MOVE就直接由ViewGroup的onTouchEvent()进行消费了,我们在onTouchEvent()返回true,那么这个touch事件的其他action以后都由这个ViewGroup进行处理。onInterceptTouchEvent()在之后的action都不会触发。

5.1.4.  结论

onInterceptTouchEvent()返回true会截取该事件,由截取该事件的View进行处理,而且onInterceptTouchEvent()在下次ACTION_DOWN来临之前都不会重复调用。

 

5.2.  测试2

5.2.1.  测试条件

在上述测试的基础上修改MyView代码:

@Overridepublic 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;}


5.2.2.  输出结果

5.2.3.  流程分析

和上述测试结果对比可以指,ViewGroup对ACTION_MOVE的处理还是会分发到子View,这是由于子View调用了getParent().requestDisallowInterceptTouchEvent(true);告诉父类不要截取事件,所以之后的action还是会分发到子View上。

5.2.4.  结论

requestDisallowInterceptTouchEvent(true)可以让父控件不截取事件。

 

6.  补充

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) {    }});

7.  总结

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。

最后附上一张流程图:



测试代码


 

 

 

0 0
原创粉丝点击