android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解。
一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->ACTION_UP
当屏幕中包含一个ViewGroup,而这个ViewGroup又包含一个子view,这个时候android系统如何处理Touch事件呢?到底是ViewGroup来处理Touch事件,还是子view来处理Touch事件呢?我只能很肯定的对你说不一定。呵呵,为什么呢?看看下面我的调查结果你就明白了。
android系统中的每个View的子类都具有下面三个和TouchEvent处理密切相关的方法:
1)public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent
2)public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent
3)public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent
当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View, TouchEvent最先到达最顶层 view 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法进行分发,如果dispatchTouchEvent返回true ,则交给这个view的onTouchEvent处理,如果dispatchTouchEvent返回 false ,则交给这个 view 的 interceptTouchEvent 方法来决定是否要拦截这个事件,如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。如果事件传递到某一层的子 view 的 onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收。而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。
通过语言描述这个处理逻辑很抽象,下面我就用代码来具体说明一下。
layout配置文件 main.xml
01 | <? xml version = "1.0" encoding = "utf-8" ?> |
02 | < test.lzqdiy.MyLinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" |
03 | android:orientation = "vertical" |
04 | android:layout_width = "fill_parent" |
05 | android:layout_height = "fill_parent" |
06 | android:gravity = "center" > |
07 | < test.lzqdiy.MyTextView |
08 | android:layout_width = "200px" |
09 | android:layout_height = "200px" |
10 | android:id = "@+id/tv" |
11 | android:text = "lzqdiy" |
12 | android:textSize = "40sp" |
13 | android:textStyle = "bold" |
14 | android:background = "#FFFFFF" |
15 | android:textColor = "#0000FF" /> |
16 | </ test.lzqdiy.MyLinearLayout > |
节点层次很简单,一个LinearLayout中添加了一个TextView。
下面是java代码:
001 | package test.lzqdiy; |
002 |
003 | import android.app.Activity; |
004 | import android.os.Bundle; |
005 |
006 | public class TestTouchEventApp extends Activity { |
007 | /** Called when the activity is first created. */ |
008 | @Override |
009 | public void onCreate(Bundle savedInstanceState) { |
010 | super .onCreate(savedInstanceState); |
011 | setContentView(R.layout.main); |
012 | } |
013 | } |
014 | package test.lzqdiy; |
015 |
016 | import android.content.Context; |
017 | import android.util.AttributeSet; |
018 | import android.util.Log; |
019 | import android.view.MotionEvent; |
020 | import android.widget.LinearLayout; |
021 |
022 | public class MyLinearLayout extends LinearLayout { |
023 | private final String TAG = "MyLinearLayout" ; |
024 |
025 | public MyLinearLayout(Context context, AttributeSet attrs) { |
026 |
027 | super (context, attrs); |
028 |
029 | Log.d(TAG, TAG); |
030 |
031 | } |
032 |
033 | @Override |
034 | public boolean dispatchTouchEvent(MotionEvent ev) { |
035 | int action = ev.getAction(); |
036 |
037 | switch (action) { |
038 |
039 | case MotionEvent.ACTION_DOWN: |
040 |
041 | Log.d(TAG, "dispatchTouchEvent action:ACTION_DOWN" ); |
042 |
043 | break ; |
044 |
045 | case MotionEvent.ACTION_MOVE: |
046 |
047 | Log.d(TAG, "dispatchTouchEvent action:ACTION_MOVE" ); |
048 |
049 | break ; |
050 |
051 | case MotionEvent.ACTION_UP: |
052 |
053 | Log.d(TAG, "dispatchTouchEvent action:ACTION_UP" ); |
054 |
055 | break ; |
056 |
057 | case MotionEvent.ACTION_CANCEL: |
058 |
059 | Log.d(TAG, "dispatchTouchEvent action:ACTION_CANCEL" ); |
060 |
061 | break ; |
062 |
063 | } |
064 | return super .dispatchTouchEvent(ev); |
065 | } |
066 |
067 | @Override |
068 | public boolean onInterceptTouchEvent(MotionEvent ev) { |
069 |
070 | int action = ev.getAction(); |
071 |
072 | switch (action) { |
073 |
074 | case MotionEvent.ACTION_DOWN: |
075 |
076 | Log.d(TAG, "onInterceptTouchEvent action:ACTION_DOWN" ); |
077 |
078 | break ; |
079 |
080 | case MotionEvent.ACTION_MOVE: |
081 |
082 | Log.d(TAG, "onInterceptTouchEvent action:ACTION_MOVE" ); |
083 |
084 | break ; |
085 |
086 | case MotionEvent.ACTION_UP: |
087 |
088 | Log.d(TAG, "onInterceptTouchEvent action:ACTION_UP" ); |
089 |
090 | break ; |
091 |
092 | case MotionEvent.ACTION_CANCEL: |
093 |
094 | Log.d(TAG, "onInterceptTouchEvent action:ACTION_CANCEL" ); |
095 |
096 | break ; |
097 |
098 | } |
099 |
100 | return false ; |
101 |
102 | } |
103 |
104 | @Override |
105 | public boolean onTouchEvent(MotionEvent ev) { |
106 |
107 | int action = ev.getAction(); |
108 |
109 | switch (action) { |
110 |
111 | case MotionEvent.ACTION_DOWN: |
112 |
113 | Log.d(TAG, "---onTouchEvent action:ACTION_DOWN" ); |
114 |
115 | break ; |
116 |
117 | case MotionEvent.ACTION_MOVE: |
118 |
119 | Log.d(TAG, "---onTouchEvent action:ACTION_MOVE" ); |
120 |
121 | break ; |
122 |
123 | case MotionEvent.ACTION_UP: |
124 |
125 | Log.d(TAG, "---onTouchEvent action:ACTION_UP" ); |
126 |
127 | break ; |
128 |
129 | case MotionEvent.ACTION_CANCEL: |
130 |
131 | Log.d(TAG, "---onTouchEvent action:ACTION_CANCEL" ); |
132 |
133 | break ; |
134 |
135 | } |
136 |
137 | return true ; |
138 | } |
139 |
140 | } |
141 | package test.lzqdiy; |
142 |
143 | import android.content.Context; |
144 | import android.util.AttributeSet; |
145 | import android.util.Log; |
146 | import android.view.MotionEvent; |
147 | import android.widget.TextView; |
148 |
149 | public class MyTextView extends TextView { |
150 |
151 | private final String TAG = "MyTextView" ; |
152 |
153 | public MyTextView(Context context, AttributeSet attrs) { |
154 |
155 | super (context, attrs); |
156 |
157 | } |
158 |
159 | @Override |
160 | public boolean dispatchTouchEvent(MotionEvent ev) { |
161 | int action = ev.getAction(); |
162 |
163 | switch (action) { |
164 |
165 | case MotionEvent.ACTION_DOWN: |
166 |
167 | Log.d(TAG, "dispatchTouchEvent action:ACTION_DOWN" ); |
168 |
169 | break ; |
170 |
171 | case MotionEvent.ACTION_MOVE: |
172 |
173 | Log.d(TAG, "dispatchTouchEvent action:ACTION_MOVE" ); |
174 |
175 | break ; |
176 |
177 | case MotionEvent.ACTION_UP: |
178 |
179 | Log.d(TAG, "dispatchTouchEvent action:ACTION_UP" ); |
180 |
181 | break ; |
182 |
183 | case MotionEvent.ACTION_CANCEL: |
184 |
185 | Log.d(TAG, "onTouchEvent action:ACTION_CANCEL" ); |
186 |
187 | break ; |
188 |
189 | } |
190 | return super .dispatchTouchEvent(ev); |
191 | } |
192 |
193 | @Override |
194 | public boolean onTouchEvent(MotionEvent ev) { |
195 |
196 | int action = ev.getAction(); |
197 |
198 | switch (action) { |
199 |
200 | case MotionEvent.ACTION_DOWN: |
201 |
202 | Log.d(TAG, "---onTouchEvent action:ACTION_DOWN" ); |
203 |
204 | break ; |
205 |
206 | case MotionEvent.ACTION_MOVE: |
207 |
208 | Log.d(TAG, "---onTouchEvent action:ACTION_MOVE" ); |
209 |
210 | break ; |
211 |
212 | case MotionEvent.ACTION_UP: |
213 |
214 | Log.d(TAG, "---onTouchEvent action:ACTION_UP" ); |
215 |
216 | break ; |
217 |
218 | case MotionEvent.ACTION_CANCEL: |
219 |
220 | Log.d(TAG, "---onTouchEvent action:ACTION_CANCEL" ); |
221 |
222 | break ; |
223 |
224 | } |
225 |
226 | return true ; |
227 |
228 | } |
229 |
230 | } |
为了指代方便,下面将MyLinearLayout简称为L,将MyTextView简称为T,L.onInterceptTouchEvent=true 表示的含义为MyLinearLayout中的onInterceptTouchEvent方法返回值为true,通过程序运行时输出的Log来说明调用时序。
第1种情况 L.onInterceptTouchEvent=false&& L.onTouchEvent=true &&T.onTouchEvent=true 输出下面的Log:
D/MyLinearLayout(11865): dispatchTouchEvent action:ACTION_DOWN
D/MyLinearLayout(11865): onInterceptTouchEvent action:ACTION_DOWN
D/MyTextView(11865): dispatchTouchEvent action:ACTION_DOWN
D/MyTextView(11865): ---onTouchEvent action:ACTION_DOWN
D/MyLinearLayout(11865): dispatchTouchEvent action:ACTION_MOVE
D/MyLinearLayout(11865): onInterceptTouchEvent action:ACTION_MOVE
D/MyTextView(11865): dispatchTouchEvent action:ACTION_MOVE
D/MyTextView(11865): ---onTouchEvent action:ACTION_MOVE
...........省略其他的ACTION_MOVE事件Log
D/MyLinearLayout(11865): dispatchTouchEvent action:ACTION_UP
D/MyLinearLayout(11865): onInterceptTouchEvent action:ACTION_UP
D/MyTextView(11865): dispatchTouchEvent action:ACTION_UP
D/MyTextView(11865): ---onTouchEvent action:ACTION_UP
结论:TouchEvent完全由TextView处理。
第2种情况 L.onInterceptTouchEvent=false&& L.onTouchEvent=true &&T.onTouchEvent=false 输出下面的Log:
D/MyLinearLayout(13101): dispatchTouchEvent action:ACTION_DOWN
D/MyLinearLayout(13101): onInterceptTouchEvent action:ACTION_DOWN
D/MyTextView(13101): dispatchTouchEvent action:ACTION_DOWN
D/MyTextView(13101): ---onTouchEvent action:ACTION_DOWN
D/MyLinearLayout(13101): ---onTouchEvent action:ACTION_DOWN
D/MyLinearLayout(13101): dispatchTouchEvent action:ACTION_MOVE
D/MyLinearLayout(13101): ---onTouchEvent action:ACTION_MOVE
...........省略其他的ACTION_MOVE事件Log
D/MyLinearLayout(13101): dispatchTouchEvent action:ACTION_UP
D/MyLinearLayout(13101): ---onTouchEvent action:ACTION_UP
结论:TextView只处理了ACTION_DOWN事件,LinearLayout处理了所有的TouchEvent。
第3种情况 L.onInterceptTouchEvent=true&& L.onTouchEvent=true 输出下面的Log:
D/MyLinearLayout(13334): dispatchTouchEvent action:ACTION_DOWN
D/MyLinearLayout(13334): onInterceptTouchEvent action:ACTION_DOWN
D/MyLinearLayout(13334): ---onTouchEvent action:ACTION_DOWN
D/MyLinearLayout(13334): dispatchTouchEvent action:ACTION_MOVE
D/MyLinearLayout(13334): ---onTouchEvent action:ACTION_MOVE
...........省略其他的ACTION_MOVE事件Log
D/MyLinearLayout(13334): dispatchTouchEvent action:ACTION_UP
D/MyLinearLayout(13334): ---onTouchEvent action:ACTION_UP
结论:LinearLayout处理了所有的TouchEvent。
第4种情况 L.onInterceptTouchEvent=true&& L.onTouchEvent=false 输出下面的Log:
D/MyLinearLayout(13452): dispatchTouchEvent action:ACTION_DOWN
D/MyLinearLayout(13452): onInterceptTouchEvent action:ACTION_DOWN
D/MyLinearLayout(13452): ---onTouchEvent action:ACTION_DOWN
结论:LinearLayout只处理了ACTION_DOWN事件,那么其他的TouchEvent被谁处理了呢?答案是LinearLayout最外层的Activity处理了TouchEvent。