笔记:Android触摸事件传递机制

(笔记:书源--Android高级进阶 顾浩鑫著 中国工信出版社)

开发时,会遇到多个View、ViewGroup嵌套的问题,此为介绍Activity、View、ViewGroup三者触摸事件传递机制。一次完整的事件传递主要包括三个阶段,分别为事件的分发、拦截和消费。

1.触摸事件的类型

  MotionEvent类:

    ACTION_DOWN:用户手指按下操作,一个按下操作标志着一个触摸事件的开始。(一次屏幕触摸操作的必需

    ACTION_MOVE:用户手指按压屏幕后,在松开之前,移动的距离超过一定的阈值,则被判断为此事件类型。一般情况下,手指的轻微移动都会触发一系列的移动事件。

    ACTION_UP:用户手指离开屏幕的操作,一次抬起操作标志着一次触摸事件的结束。(一次屏幕触摸操作的必需

2.事件传递的三个阶段

  1)分发Dispatch-->dispatchTouchEvent

    

 public boolean dispatchTouchEvent(MotionEvent ev)

  根据当前视图的具体实现逻辑,来决定是直接消费这个事件还是将事件继续分发给子视图处理,方法返回值为true表示事件被当前视图消费掉,不再分发事件;返回值为super.dispatchTouchEvent表示继续分发该事件。

  如果当前视图是ViewGroup及子类,则调用OnInterceptTouchEvent方法判定是否拦截该事件。

  2)拦截Intercept-->onInterceptTouchEvent

    该方法旨在ViewGroup及其子类中存在,在View和Activity中是不存在的。

public boolean onInterceptTouchEvent(MotionEvent event)

  也是通过返回值来决定是否拦截对应的事件,根据具体的实现逻辑,返回true表示拦截这个事件,不继续分发给子视图,同时交由自身的onTouchEvent方法进行消费;返回false或者super.onInterceptTouchEvent表示不对事件进行拦截,需要继续传递给子视图。

  3)消费Consume-->onTouchEvent

public boolean onTouchEvent(MotionEvent event)

  返回值为true时表示当前视图可以处理对应的事件,事件将不会向上传递给父视图;返回值为false表示当前视图不处理这个事件,事件会被传递给父视图的onTouchEvent方法进行处理。

  Android系统中,拥有事件传递处理能力的类有以下三种:

  Activity:  拥有dispatchTouchEvent , onTouchEvent

  ViewGroup: 拥有dispatchTouchEvent , onInterceptTouchEvent , onTouchEvent

  View: 拥有 dispatchTouchEvent , onTouchEvent

3.View事件传递机制

  这里所说的View专指除ViewGroup外的View控件

  (在这里书上继承TextView,不过AS会报错,需要改成AppCompatTextView

 1 import android.content.Context;
 2 import android.support.v7.widget.AppCompatTextView;
 3 import android.util.AttributeSet;
 4 import android.util.Log;
 5 import android.view.MotionEvent;
 6 
 7 /**
 8  * 将每个事件的触发都打印日志,方便了解事件传递的流程
 9  * */
10 public class MyTextView extends AppCompatTextView {
11     private static final String TAG="MyTextView";
12 
13     public MyTextView(Context context) {
14         super(context);
15     }
16 
17     public MyTextView(Context context, AttributeSet attrs){
18         super(context,attrs);
19     }
20 
21     public boolean dispatchTouchEvent(MotionEvent ev){
22         switch (ev.getAction()){
23             case MotionEvent.ACTION_DOWN:
24                 Log.e(TAG,"dispatchTouchEvent ACTION_DOWN");
25                 break;
26             case MotionEvent.ACTION_MOVE:
27                 Log.e(TAG,"dispatchTouchEvent ACTION_MOVE");
28                 break;
29             case MotionEvent.ACTION_UP:
30                 Log.e(TAG,"dispatchTouchEvent ACTION_UP");
31                 break;
32             case MotionEvent.ACTION_CANCEL:
33                 Log.e(TAG,"dispatchTouchEvent ACTION_CANCEL");
34                 break;
35             default:
36                 break;
37         }
38         return super.dispatchTouchEvent(ev);
39     }
40 
41 
42     public boolean onTouchEvent(MotionEvent event){
43         switch(event.getAction()){
44             case MotionEvent.ACTION_DOWN:
45                 Log.e(TAG,"onTouchEvent ACTION_DOWN");
46                 break;
47             case MotionEvent.ACTION_MOVE:
48                 Log.e(TAG,"onTouchEvent ACTION_MOVE");
49                 break;
50             case MotionEvent.ACTION_UP:
51                 Log.e(TAG,"onTouchEvent ACTION_UP");
52                 break;
53             case MotionEvent.ACTION_CANCEL:
54                 Log.e(TAG,"onTouchEvent ACTION_CANCEL");
55                 break;
56             default:
57                 break;
58         }
59         return super.onTouchEvent(event);
60     }
61 }
1 <com.example.zy.internetandlife.ViewTestDemo.MyTextView
2         android:layout_width="wrap_content"
3         android:layout_height="18dp"
4         android:text="MyTextView"
5         android:id="@+id/my_text_view"
6         app:layout_constraintBottom_toBottomOf="parent"
7         app:layout_constraintLeft_toLeftOf="parent"
8         app:layout_constraintRight_toRightOf="parent"
9         app:layout_constraintTop_toTopOf="parent" />
 1 public class MainActivity extends AppCompatActivity implements View.OnClickListener,View.OnTouchListener {
 2     private static final String TAG="MainActivity";
 3     private MyTextView myTextView;
 4     @Override
 5     protected void onCreate(Bundle savedInstanceState) {
 6         super.onCreate(savedInstanceState);
 7         setContentView(R.layout.activity_main);
 8         myTextView=(MyTextView)findViewById(R.id.my_text_view);
 9         myTextView.setOnClickListener(this);
10         myTextView.setOnTouchListener(this);
11     }
12     @Override
13     public boolean dispatchTouchEvent(MotionEvent ev){
14         switch (ev.getAction()){
15             case MotionEvent.ACTION_DOWN:
16                 Log.e(TAG,"dispatchTouchEvent ACTION_DOWN");
17                 break;
18             case MotionEvent.ACTION_MOVE:
19                 Log.e(TAG,"dispatchTouchEvent ACTION_MOVE");
20                 break;
21             case MotionEvent.ACTION_UP:
22                 Log.e(TAG,"dispatchTouchEvent ACTION_UP");
23                 break;
24             case MotionEvent.ACTION_CANCEL:
25                 Log.e(TAG,"dispatchTouchEvent ACTION_CANCEL");
26                 break;
27             default:
28                 break;
29         }
30         return super.dispatchTouchEvent(ev);
31     }
32     @Override
33     public boolean onTouchEvent(MotionEvent event){
34         switch(event.getAction()){
35             case MotionEvent.ACTION_DOWN:
36                 Log.e(TAG,"onTouchEvent ACTION_DOWN");
37                 break;
38             case MotionEvent.ACTION_MOVE:
39                 Log.e(TAG,"onTouchEvent ACTION_MOVE");
40                 break;
41             case MotionEvent.ACTION_UP:
42                 Log.e(TAG,"onTouchEvent ACTION_UP");
43                 break;
44             case MotionEvent.ACTION_CANCEL:
45                 Log.e(TAG,"onTouchEvent ACTION_CANCEL");
46                 break;
47             default:
48                 break;
49         }
50         return super.onTouchEvent(event);
51     }
52     @Override
53     public void onClick(View v) {
54         switch(v.getId()){
55             case R.id.my_text_view:
56                 Log.e(TAG, "MyTextView onClick" );
57                 break;
58             default:
59                 break;
60         }
61     }
62 
63     @Override
64     public boolean onTouch(View v, MotionEvent event) {
65         switch (v.getId()){
66             case R.id.my_text_view:
67                 switch (event.getAction()){
68                     case MotionEvent.ACTION_DOWN:
69                         Log.e(TAG,"MyTextView onTouch ACTION_DOWN");
70                         break;
71                     case MotionEvent.ACTION_MOVE:
72                         Log.e(TAG,"MyTextViEW onTouch ACTION_MOVE");
73                         break;
74                     case MotionEvent.ACTION_UP:
75                         Log.e(TAG,"MyTextView onTouch ACTION_UP");
76                         break;
77                     default:
78                         break;
79                 }
80                 break;
81             default:
82                 break;
83         }
84         return false;
85     }
86 }
View Code

  在此,存在三种情况:

  1)返回false;

  2)返回true;

  3)父类的同名方法

  

  结论:

    1)触摸事件的传递流程是从dispatchTouchEvent开始的,如果不进行人为干预,则会以找嵌套层次由外向内传递,到达最内层的View时,由onTouchEvent方法处理,如果该方法能够消费该事件,则返回true,否则false.这时事件会重新向外层传递,并由外层View的onTouchEvent方法进行处理.

    2)如果进行人为干预,事件处理函数返回true,则会导致事件题前被消费掉,内层View将不会收到这个事件。

    3)View触发事件顺序是先执行onTouch,在最后才执行onClick。如果onTouch返回true,则事件不会继续传递,最后也不会调用onClick方法;如果onTouch返回false,则事件继续传递。

4.ViewGroup的事件传递机制

  ViewGroup和View的唯一区别是多了一个onInterceptTouchEvent方法。

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <com.example.zy.internetandlife.ViewTestDemo.MyRelativeLayout 
 3     xmlns:android="http://schemas.android.com/apk/res/android"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent"
 6     xmlns:tools="http://schemas.android.com/tools"
 7     tools:context="com.example.zy.internetandlife.MainActivity">
 8 
 9 
10     <com.example.zy.internetandlife.ViewTestDemo.MyTextView
11         android:id="@+id/my_text_view"
12         android:textSize="20sp"
13         android:text="Hello ViewSet!"
14         android:layout_width="wrap_content"
15         android:layout_height="wrap_content" />
16 </com.example.zy.internetandlife.ViewTestDemo.MyRelativeLayout>
 1 public class MyRelativeLayout extends RelativeLayout {
 2 
 3     private static final String TAG="MyRelativeLayout";
 4 
 5     public MyRelativeLayout(Context context) {
 6         super(context);
 7     }
 8 
 9     public MyRelativeLayout(Context context, AttributeSet attrs) {
10         super(context, attrs);
11     }
12 
13     public boolean dispatchTouchEvent(MotionEvent ev){
14         switch (ev.getAction()){
15             case MotionEvent.ACTION_DOWN:
16                 Log.e(TAG,"dispatchTouchEvent ACTION_DOWN");
17                 break;
18             case MotionEvent.ACTION_MOVE:
19                 Log.e(TAG,"dispatchTouchEvent ACTION_MOVE");
20                 break;
21             case MotionEvent.ACTION_UP:
22                 Log.e(TAG,"dispatchTouchEvent ACTION_UP");
23                 break;
24             case MotionEvent.ACTION_CANCEL:
25                 Log.e(TAG,"dispatchTouchEvent ACTION_CANCEL");
26                 break;
27             default:
28                 break;
29         }
30         return super.dispatchTouchEvent(ev);
31     }
32 
33     public boolean onTouchEvent(MotionEvent event){
34         switch(event.getAction()){
35             case MotionEvent.ACTION_DOWN:
36                 Log.e(TAG,"onTouchEvent ACTION_DOWN");
37                 break;
38             case MotionEvent.ACTION_MOVE:
39                 Log.e(TAG,"onTouchEvent ACTION_MOVE");
40                 break;
41             case MotionEvent.ACTION_UP:
42                 Log.e(TAG,"onTouchEvent ACTION_UP");
43                 break;
44             case MotionEvent.ACTION_CANCEL:
45                 Log.e(TAG,"onTouchEvent ACTION_CANCEL");
46                 break;
47             default:
48                 break;
49         }
50         return super.onTouchEvent(event);
51     }
52 
53     public boolean onIterceptTouchEvent(MotionEvent ev){
54         switch(ev.getAction()){
55             case MotionEvent.ACTION_DOWN:
56                 Log.e(TAG,"OnInterceptTouchEvent ACTION_DOWN");
57                 break;
58             case MotionEvent.ACTION_MOVE:
59                 Log.e(TAG,"OnInterceptTouchEvent ACTION_MOVE");
60                 break;
61             case MotionEvent.ACTION_UP:
62                 Log.e(TAG,"OnInterceptTouchEvent ACTION_UP");
63                 break;
64             default:
65                 break;
66         }
67         return super.onInterceptTouchEvent(ev);
68     }
69 }
View Code

  不一样的地方:MainActivity和MyTextView之间增加了一层MyRelativeLayout。

  结论:

  1)触摸事件的传递顺序是由Activity到ViewGroup,再由ViewGroup递归传递给它的子View。

  2)ViewGroup通过InterceptTouchEvent方法对事件进行拦截,如果该方法返回true,则事件不会继续传递给子View,如果返回false或者super.onInterceptTouchEvent,则事件继续传递给子View.

  3)在子View中对事件进行消费后,ViewGroup将接收不到任何事件。

猜你喜欢

转载自www.cnblogs.com/fordreamInsisting/p/10499878.html