android触发事件传递机制

时间:2020-12-02 22:34:45

一 事件传递的三个阶段
1 分发(Dispatch):
事件的分发对应着dispatchTouchEvent方法,在Android系统中,所有的触摸事件的分发都是由改方法分发。

public booleandispatchTouchEvent(MotionEvent event) {..}

说明:在这个方法中,当前视图将根据返回值决定是否把这个事件继续传给子视图,如果返回true当前视图会直接消费掉该事件,不再分发给其子视图;如果返回super.dispatchTouchEvent(event) ,当前视图会把该事件分发给其子视图。如果当前视图是ViewGroup或其子类的话,还要调用onInterceptTouchEvent方法判断该事件是否被拦截。
2 拦截(Intercept):
事件的拦截,对应如下的方法:

public booleanonInterceptTouchEvent(MotionEvent ev) {...}

说明:这个方法只有在ViewGroup及其子类中有,它们通过返回值来判断是否拦截当前事件,如果返回true会拦截当前事件,不再分发给它们的子视图,同时会调用onTouchEvent消费该事件;如果返回false或super.onInterceptTouchEvent,那么它们会把当前事件分发给它们的子视图。
3 消费:
事件的消费,对应如下的方法:

public booleanonTouchEvent(MotionEvent ev) {...}

说明:该方法返回true则表示当前视图处理相应的事件,事件不会向上传递给父视图;返回false则表示当前视图不处理相应的事件,会把这个事件向上传递给父视图onTouchEvent方法进行处理。
Android系统中有事件传递能力的类有:
Activity:有dispatchTouchEvent、onTouchEvent两个方法。
ViewGroup:有dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent三个方法。
View:有dispatchTouchEvent、onTouchEvent两个方法。如下:
二 View事件传递
1 自定义TextView

public class MyTextView extends TextView {
public static final String TAG = MyTextView.class.getSimpleName() + "======> ";
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"dispatchTouchEvent->ACTION_DOWN ");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"dispatchTouchEvent->ACTION_MOVE ");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"dispatchTouchEvent->ACTION_UP ");
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"onTouchEvent->ACTION_DOWN ");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"onTouchEvent->ACTION_MOVE ");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"onTouchEvent->ACTION_UP ");
break;
}
return super.onTouchEvent(event);
}
}

2布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.eric.project.MainActivity">
<com.eric.project.event.MyTextView
android:id="@+id/txt_eventView"
android:layout_centerInParent="true"
android:textSize="36sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="View 事件传递" />
</RelativeLayout>

3 Activity代码:

public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName() + "======> ";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView txt_eventView = (TextView) findViewById(R.id.txt_eventView);
txt_eventView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG," onClick ");
}
});
//如果不设置触摸监听不会触发onTouchEvent方法
txt_eventView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG," onTouch->ACTION_DOWN ");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG," onTouch->ACTION_MOVE ");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG," onTouch->ACTION_UP ");
break;
}
return false;
}
});
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"dispatchTouchEvent->ACTION_DOWN ");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"dispatchTouchEvent->ACTION_MOVE ");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"dispatchTouchEvent->ACTION_UP ");
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"onTouchEvent->ACTION_DOWN ");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"onTouchEvent->ACTION_MOVE ");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"onTouchEvent->ACTION_UP ");
break;
}
return super.onTouchEvent(event);
}
}
4 打印结果如下:
01-31 05:09:36.158 14865-14865/com.eric.project E/MainActivity======>: dispatchTouchEvent->ACTION_DOWN
01-31 05:09:36.158 14865-14865/com.eric.project E/MyTextView======>: dispatchTouchEvent->ACTION_DOWN
01-31 05:09:36.159 14865-14865/com.eric.project E/MainActivity======>: onTouch->ACTION_DOWN
01-31 05:09:36.159 14865-14865/com.eric.project E/MyTextView======>: onTouchEvent->ACTION_DOWN
01-31 05:09:36.254 14865-14865/com.eric.project E/MainActivity======>: dispatchTouchEvent->ACTION_UP
01-31 05:09:36.254 14865-14865/com.eric.project E/MyTextView======>: dispatchTouchEvent->ACTION_UP
01-31 05:09:36.254 14865-14865/com.eric.project E/MainActivity======>: onTouch->ACTION_UP
01-31 05:09:36.254 14865-14865/com.eric.project E/MyTextView======>: onTouchEvent->ACTION_UP
01-31 05:09:36.257 14865-14865/com.eric.project E/MainActivity======>: onClick

从上面得出如下结论:
(1)触摸事件的传递流程是从dispatchTouchEvent开始,如果不进行干预(即返回super.dispatchTouchEvent(ev)),则事件将会依照潜逃层次从外层向内层传递,到达最底层的View时,就由它的onTouchEvent方法进行处理,该方法如果能够消费该事件,就会返回true,如果处理不了,就返回false,这时会重新向外层传递,并由外层View的onTouchEvent方法进行处理,以此类推。
(2)如果事件在向内层传递的过程中人为干预了,时间函数返回true,则会导致事件提前被消费掉,内层的View不能收到事件。
(3)如果最内层的View能够接受到事件,那么它先执行的是onTouch方法,然后才执行onClick方法。如果onTouch返回true,则事件不会继续传递,最后也不会调用onClick方法;如果返回super.onTouch或false,事件会继续传递。流程图如下:

android触发事件传递机制
三 ViewGroup事件传递
1 自定义ViewGroup

public class MyViewGroup extends LinearLayout {
public static final String TAG = MyViewGroup.class.getSimpleName() + "======> ";
public MyViewGroup(Context context) {
super(context);
}
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"dispatchTouchEvent->ACTION_DOWN ");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"dispatchTouchEvent->ACTION_MOVE ");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"dispatchTouchEvent->ACTION_UP ");
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"onTouchEvent->ACTION_DOWN ");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"onTouchEvent->ACTION_MOVE ");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"onTouchEvent->ACTION_UP ");
break;
}
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"onInterceptTouchEvent->ACTION_DOWN ");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"onInterceptTouchEvent->ACTION_MOVE ");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG,"onInterceptTouchEvent->ACTION_UP ");
break;
}
return super.onInterceptTouchEvent(event);
}
}

2 布局文件:

<?xml version="1.0" encoding="utf-8"?>
<com.eric.project.event.MyViewGroup
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
tools:context="com.eric.project.MainActivity">
<com.eric.project.event.MyTextView
android:id="@+id/txt_eventView"
android:layout_centerInParent="true"
android:textSize="36sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="View 事件传递" />
</com.eric.project.event.MyViewGroup>

4 打印日志:

02-01 15:49:55.721 23305-23305/com.eric.project E/MainActivity======>: dispatchTouchEvent->ACTION_DOWN
02-01 15:49:55.722 23305-23305/com.eric.project E/MyViewGroup======>: dispatchTouchEvent->ACTION_DOWN
02-01 15:49:55.722 23305-23305/com.eric.project E/MyViewGroup======>: onInterceptTouchEvent->ACTION_DOWN
02-01 15:49:55.722 23305-23305/com.eric.project E/MyTextView======>: dispatchTouchEvent->ACTION_DOWN
02-01 15:49:55.722 23305-23305/com.eric.project E/MainActivity======>: onTouch->ACTION_DOWN
02-01 15:49:55.722 23305-23305/com.eric.project E/MyTextView======>: onTouchEvent->ACTION_DOWN
02-01 15:49:55.745 23305-23305/com.eric.project E/MainActivity======>: dispatchTouchEvent->ACTION_MOVE
02-01 15:49:55.745 23305-23305/com.eric.project E/MyViewGroup======>: dispatchTouchEvent->ACTION_MOVE
02-01 15:49:55.745 23305-23305/com.eric.project E/MyViewGroup======>: onInterceptTouchEvent->ACTION_MOVE
02-01 15:49:55.745 23305-23305/com.eric.project E/MyTextView======>: dispatchTouchEvent->ACTION_MOVE
02-01 15:49:55.745 23305-23305/com.eric.project E/MainActivity======>: onTouch->ACTION_MOVE
02-01 15:49:55.745 23305-23305/com.eric.project E/MyTextView======>: onTouchEvent->ACTION_MOVE
02-01 15:49:55.746 23305-23305/com.eric.project E/MainActivity======>: dispatchTouchEvent->ACTION_UP
02-01 15:49:55.746 23305-23305/com.eric.project E/MyViewGroup======>: dispatchTouchEvent->ACTION_UP
02-01 15:49:55.746 23305-23305/com.eric.project E/MyViewGroup======>: onInterceptTouchEvent->ACTION_UP
02-01 15:49:55.746 23305-23305/com.eric.project E/MyTextView======>: dispatchTouchEvent->ACTION_UP
02-01 15:49:55.746 23305-23305/com.eric.project E/MainActivity======>: onTouch->ACTION_UP
02-01 15:49:55.746 23305-23305/com.eric.project E/MyTextView======>: onTouchEvent->ACTION_UP
02-01 15:49:55.747 23305-23305/com.eric.project E/MainActivity======>: onClick
从上面得出如下结论:
(1)触摸事件传递的顺序Activity到ViewGroup,再传递给ViewGroup的子类View。
(2)事件传递的过程,ViewGroup调用 onInterceptTouchEvent方法判断事件是否被拦截,如果 onInterceptTouchEvent返回true,事件被拦截,不会再传递给其子View,否则会继续传递。
(3)View接收到事件,由onTouchEvent方法进行处理,View能够处理,事件没有上传给ViewGroup。
流程图如下:
android触发事件传递机制
四 事件传递总结:
android触发事件传递机制