码迷,mamicode.com
首页 > 移动开发 > 详细

android事件分发流程

时间:2016-07-13 16:50:09      阅读:226      评论:0      收藏:0      [点我收藏+]

标签:

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代码:

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


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。

最后附上一张流程图:

技术分享


测试代码


 

 

 

android事件分发流程

标签:

原文地址:http://blog.csdn.net/qq282330332/article/details/51887436

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!