http://ipjmc.iteye.com/blog/1694146
在Android里Touch是很常用的事件,尤其實在自定義控件中,要實現一些動態的效果,往往要對Touch進行處理。Android中主要有3個地方可以處理Touch事件:
一、在View里,有兩個回調函數
- public boolean dispatchTouchEvent(MotionEvent ev);
- public boolean onTouchEvent(MotionEvent ev);
二、在ViewGroup里,有三個回調函數
- public boolean dispatchTouchEvent(MotionEvent ev);
- public boolean onInterceptTouchEvent(MotionEvent ev);
- public boolean onTouchEvent(MotionEvent ev);
三、在Activity里,有兩個回調函數
- public boolean dispatchTouchEvent(MotionEvent ev);
- public boolean onTouchEvent(MotionEvent ev);
在這三個不同的地方,它們對Touch事件的處理流程很相似,但也有不同的地方。
在本節,就先研究View對Touch的處理過程。首先,Touch事件先到達dispatchTouchEvent(),我們來看看View.dispatchTouchEvent()的源碼,這里面涉及到View的onTouchListener,和onTouchEvent()。
- public boolean dispatchTouchEvent(MotionEvent event) {
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onTouchEvent(event, 0);
- }
- if (onFilterTouchEventForSecurity(event)) {
- //noinspection SimplifiableIfStatement
- ListenerInfo li = mListenerInfo;
- if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
- && li.mOnTouchListener.onTouch(this, event)) {
- return true;
- }
- if (onTouchEvent(event)) {
- return true;
- }
- }
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
- }
- return false;
- }
從源碼中,我們可以看出View的onTouchListener和onTouchEvent都是在這里被調用的。如果View的touchListener返回true,dispatchTouchEvent()直接就返回,連onTouchEvent都不會被調用了。只有View沒有設置onTouchListener,或者touchListener.onTouch()返回false,才會調用onTouchEvent()。
我們還可以看出,如果onTouchEvent()被執行了的話,dispatchTouchEvent()的返回值就是onTouchEvent()的返回值。事實上,真正起作用的也就是dispatchTouchEvent(),onTouchEvent()只是被dispatchTouchEvent()調用了而已。關於這個返回值的作用,請往下看。
我們需要一些實驗,自定義控件還使用之前的畫板
- package com.ipjmc.vgdemo;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.MotionEvent;
- import android.view.View;
- public class CustomView extends View {
- private static final String TAG = "CustomView";
- private int mLastX, mLastY;
- private int mCurrX, mCurrY;
- private Bitmap mBitmap;
- private Paint mPaint;
- public CustomView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mPaint = new Paint();
- mPaint.setStrokeWidth(6);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- int width = getWidth();
- int height = getHeight();
- if (mBitmap == null) {
- mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- }
- Canvas tmpCanvas = new Canvas(mBitmap);
- tmpCanvas.drawLine(mLastX, mLastY, mCurrX, mCurrY, mPaint);
- canvas.drawBitmap(mBitmap, 0, 0, mPaint);
- }
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- Utils.log(TAG, "dispatchTouchEvent", event.getAction());
- return super.dispatchTouchEvent(event);
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- mLastX = mCurrX;
- mLastY = mCurrY;
- mCurrX = (int) event.getX();
- mCurrY = (int) event.getY();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mLastX = mCurrX;
- mLastY = mCurrY;
- break;
- default:
- break;
- }
- invalidate();
- return true;
- }
- }
我們在畫板上畫一條線,看一下日志 
如果我們把dispatchTouchEvent的返回值改為true,無論onTouchEvent()的返回值是什么,都不影響。
現在,我們把dispatchTouchEvent的返回值,改為false看看會怎么樣
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- Utils.log(TAG, "dispatchTouchEvent", event.getAction());
- super.dispatchTouchEvent(event);
- return false;
- }
我們在畫板上畫一條線,看一下日志 
我們可以看到,只在Touch事件是ACTION_DOWN的時候,打印了一條日志,而且畫板上也沒有畫出一條線。這是說明在ACTION_DOWN的時候,如果dispatchTouchEvent返回false,那么這個View就接收不到后面的觸屏事件了。
如果我們這樣改呢?dispatchTouchEvent()只在ACTION_DOWN的時候,返回true,其他時候返回false。答案是一切又恢復正常了。
- public boolean dispatchTouchEvent(MotionEvent event) {
- Utils.log(TAG, "dispatchTouchEvent", event.getAction());
- super.dispatchTouchEvent(event);
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- return true;
- }
- return false;
- }
可以這樣理解。每一個觸屏事件都必須是以ACTION_DOWN作為開頭,后面跟一系列的ACTION_MOVE,最后再有一個ACTION_UP(或ACTION_CANCEL),標識觸屏事件結束。所以Android就在ACTION_DOWN的時候做文章,官方文檔對dispatchTouchEvent的返回值的解釋是:True if the event was handled by the view, false otherwise。我們可以簡單的理解為如果返回true,就說明它需要處理這個事件,就讓它接收所有的觸屏事件,否則,說明它不用處理,也就不讓它接收后續的觸屏事件了。

