2015-11-26 17:00:22

前言:Android的Touch事件传递和View的实现紧密相连,因此理解Touch事件的传递,有助于我们更好的理解View的工作原理。

1. 几个重要的方法:

View.java

=============

dispatchTouchEvent():用来分发、传递Touch事件,如果Touch事件被当前View处理了,就返回true,否则返回false。

 1     /**
 2      * Pass the touch screen motion event down to the target view, or this
 3      * view if it is the target.
 4      * 将Touch事件传递到目标View,如果自己就是目标View的话,就给自己
 5      * @param event The motion event to be dispatched.
 6      * @return True if the event was handled by the view, false otherwise.
 7      */
 8     public boolean dispatchTouchEvent(MotionEvent event) {
 9         // If the event should be handled by accessibility focus first.
10         if (event.isTargetAccessibilityFocus()) {
11             // We don't have focus or no virtual descendant has it, do not handle the event.
12             if (!isAccessibilityFocusedViewOrHost()) {
13                 return false;
14             }
15             // We have focus and got the event, then use normal event dispatch.
16             event.setTargetAccessibilityFocus(false);
17         }
18 
19         boolean result = false;
20 
21         if (mInputEventConsistencyVerifier != null) {
22             mInputEventConsistencyVerifier.onTouchEvent(event, 0);
23         }
24 
25         final int actionMasked = event.getActionMasked();
26         if (actionMasked == MotionEvent.ACTION_DOWN) {
27             // Defensive cleanup for new gesture
28             stopNestedScroll();
29         }
30 
31         if (onFilterTouchEventForSecurity(event)) {
32             //noinspection SimplifiableIfStatement
33             ListenerInfo li = mListenerInfo;
34             if (li != null && li.mOnTouchListener != null
35                     && (mViewFlags & ENABLED_MASK) == ENABLED
36                     && li.mOnTouchListener.onTouch(this, event)) { (1)
37                 result = true;
38             }
39 
40             if (!result && onTouchEvent(event)) { (2)
41                 result = true;
42             }
43         }
44 
45         if (!result && mInputEventConsistencyVerifier != null) {
46             mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
47         }
48 
49         // Clean up after nested scrolls if this is the end of a gesture;
50         // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
51         // of the gesture.
52         if (actionMasked == MotionEvent.ACTION_UP ||
53                 actionMasked == MotionEvent.ACTION_CANCEL ||
54                 (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
55             stopNestedScroll();
56         }
57 
58         return result;
59     }

通俗理解:

    该方法是View上Touch事件传递的第一道防线,现在只需要记住:

    (1)处代码调用了OnTouchListener,如果你给当前View设置了setOnTouchListener(),那么,此处会被调用;

    (2)如果你设置的onTouchListener的onTouch方法,返回的是false,那么此处有机会调用当前View的onTouchEvent方法。如果返回true,不会调用onTouchEvent,同时你设置的onClickListener也不会得到调用,这点我们接下来分析。

onTouchEvent():当前View收到Touch事件后,真正使用Touch事件的地方。

  1     public boolean onTouchEvent(MotionEvent event) {
  2         final float x = event.getX();
  3         final float y = event.getY();
  4         final int viewFlags = mViewFlags;
  5 
  6         if ((viewFlags & ENABLED_MASK) == DISABLED) {
  7             if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
  8                 setPressed(false);
  9             }
 10             // A disabled view that is clickable still consumes the touch
 11             // events, it just doesn't respond to them.
 12             return (((viewFlags & CLICKABLE) == CLICKABLE ||
 13                     (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
 14         }
 15 
 16         if (mTouchDelegate != null) {
 17             if (mTouchDelegate.onTouchEvent(event)) {
 18                 return true;
 19             }
 20         }
 21 
 22         if (((viewFlags & CLICKABLE) == CLICKABLE ||
 23                 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
 24             switch (event.getAction()) {
 25                 case MotionEvent.ACTION_UP:
 26                     boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
 27                     if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
 28                         // take focus if we don't have it already and we should in
 29                         // touch mode.
 30                         boolean focusTaken = false;
 31                         if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
 32                             focusTaken = requestFocus();
 33                         }
 34 
 35                         if (prepressed) {
 36                             // The button is being released before we actually
 37                             // showed it as pressed.  Make it show the pressed
 38                             // state now (before scheduling the click) to ensure
 39                             // the user sees it.
 40                             setPressed(true, x, y);
 41                        }
 42 
 43                         if (!mHasPerformedLongPress) {
 44                             // This is a tap, so remove the longpress check
 45                             removeLongPressCallback();
 46 
 47                             // Only perform take click actions if we were in the pressed state
 48                             if (!focusTaken) {
 49                                 // Use a Runnable and post this rather than calling
 50                                 // performClick directly. This lets other visual state
 51                                 // of the view update before click actions start.
 52                                 if (mPerformClick == null) {
 53                                     mPerformClick = new PerformClick();
 54                                 }
 55                                 if (!post(mPerformClick)) {
 56                                     performClick(); (1)
 57                                 }
 58                             }
 59                         }
 60 
 61                         if (mUnsetPressedState == null) {
 62                             mUnsetPressedState = new UnsetPressedState();
 63                         }
 64 
 65                         if (prepressed) {
 66                             postDelayed(mUnsetPressedState,
 67                                     ViewConfiguration.getPressedStateDuration());
 68                         } else if (!post(mUnsetPressedState)) {
 69                             // If the post failed, unpress right now
 70                             mUnsetPressedState.run();
 71                         }
 72 
 73                         removeTapCallback();
 74                     }
 75                     break;
 76 
 77                 case MotionEvent.ACTION_DOWN:
 78                     mHasPerformedLongPress = false;
 79 
 80                     if (performButtonActionOnTouchDown(event)) {
 81                         break;
 82                     }
 83 
 84                     // Walk up the hierarchy to determine if we're inside a scrolling container.
 85                     boolean isInScrollingContainer = isInScrollingContainer();
 86 
 87                     // For views inside a scrolling container, delay the pressed feedback for
 88                     // a short period in case this is a scroll.
 89                     if (isInScrollingContainer) {
 90                         mPrivateFlags |= PFLAG_PREPRESSED;
 91                         if (mPendingCheckForTap == null) {
 92                             mPendingCheckForTap = new CheckForTap();
 93                         }
 94                         mPendingCheckForTap.x = event.getX();
 95                         mPendingCheckForTap.y = event.getY();
 96                         postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
 97                     } else {
 98                         // Not inside a scrolling container, so show the feedback right away
 99                         setPressed(true, x, y);
100                         checkForLongClick(0); (2)
101                     }
102                     break;
103 
104                 case MotionEvent.ACTION_CANCEL:
105                     setPressed(false);
106                     removeTapCallback();
107                     removeLongPressCallback();
108                     break;
109 
110                 case MotionEvent.ACTION_MOVE:
111                     drawableHotspotChanged(x, y);
112 
113                     // Be lenient about moving outside of buttons
114                     if (!pointInView(x, y, mTouchSlop)) {
115                         // Outside button
116                         removeTapCallback();
117                         if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
118                             // Remove any future long press/tap checks
119                             removeLongPressCallback();
120 
121                             setPressed(false);
122                         }
123                     }
124                     break;
125             }
126 
127             return true;
128         }
129 
130         return false;
131     }

在onTouchEvent()方法中,分别处理不同类型的Touch事件。注意:

(1)当前View处理Click事件的地方,没错,就是在MotionEvent.Action_UP的时候处理的;

(2)当前View处理LongClick事件的地方,在MotionEvent.Action_DOWN的时候处理的;

ViewGroup.java

=============

ViewGroup是继承自View的,当然也继承了上面那两个方法。ViewGroup不仅是一个View,也是一个可以拥有Children View的View Container,所以它重写了dispatchTouchEvent()方法,但是没有重写onTouchEvent()方法。同时,ViewGroup觉得自己好歹是个Container,得有点特权嘛,所以给自己搞了个onInterceptTouchEvent()方法,如下:

 1     public boolean dispatchTouchEvent(MotionEvent ev) {
 2         
 3         boolean handled = false;
 4         if (onFilterTouchEventForSecurity(ev)) {
 5             final int action = ev.getAction();
 6             final int actionMasked = action & MotionEvent.ACTION_MASK;
 7 
 8             // Handle an initial down.
 9             if (actionMasked == MotionEvent.ACTION_DOWN) {
10                 // Throw away all previous state when starting a new touch gesture.
11                 // The framework may have dropped the up or cancel event for the previous gesture
12                 // due to an app switch, ANR, or some other state change.
13                 cancelAndClearTouchTargets(ev);
14                 resetTouchState();
15             }
16 
17             // Check for interception.
18             final boolean intercepted;
19             if (actionMasked == MotionEvent.ACTION_DOWN
20                     || mFirstTouchTarget != null) {
21                 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
22                 if (!disallowIntercept) {
23                     intercepted = onInterceptTouchEvent(ev); (1)
24                     ev.setAction(action); // restore action in case it was changed
25                 } else {
26                     intercepted = false;
27                 }
28             } else {
29                 // There are no touch targets and this action is not an initial down
30                 // so this view group continues to intercept touches.
31                 intercepted = true;
32             }
33 
34             // Check for cancelation.
35             final boolean canceled = resetCancelNextUpFlag(this)
36                     || actionMasked == MotionEvent.ACTION_CANCEL;
37 
38             // Update list of touch targets for pointer down, if needed.
39             final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
40             TouchTarget newTouchTarget = null;
41             boolean alreadyDispatchedToNewTouchTarget = false;
42             if (!canceled && !intercepted) { (2)
43         ……
44                
45     }

ViewGroup的dispatchTouchView()方法,加了一些自己的判断,最终调用的仍然是View中的dispatchTouchView(),先关注一下红色代码:

(1)ViewGroup调用了自己的onInterceptTouchEvent();

(2)只有onInterceptTouchEvent()返回false时,才会继续执行下面的代码。这里可以这么理解,onInterceptTouchEvent()返回true,表示该Touch事件被拦截,不会向Children View传递,分发给自己handle,如果返回false,才会继续向Children View传递。

onInterceptTouchEvent(MotionEvent ev):

1     /* @param ev The motion event being dispatched down the hierarchy.
2      * @return Return true to steal motion events from the children and have
3      * them dispatched to this ViewGroup through onTouchEvent().
4      * The current target will receive an ACTION_CANCEL event, and no further
5      * messages will be delivered here.
6      */
7     public boolean onInterceptTouchEvent(MotionEvent ev) {
8         return false;
9     }

此方法默认返回false。

2. 写个小Demo,源码如下:

MyLinear1.java

 1 public class MyLinear1 extends LinearLayout {
 2     public MyLinear1(Context context) {
 3         super(context);
 4     }
 5 
 6     public MyLinear1(Context context, AttributeSet attrs) {
 7         super(context, attrs);
 8     }
 9 
10     public MyLinear1(Context context, AttributeSet attrs, int defStyleAttr) {
11         super(context, attrs, defStyleAttr);
12     }
13 
14     @Override
15     protected void onLayout(boolean changed, int l, int t, int r, int b) {
16         super.onLayout(changed, l, t, r, b);
17     }
18 
19     boolean isDispatch = true;
20 
21     @Override
22     public boolean dispatchTouchEvent(MotionEvent ev) {
23         Log.e("David--MyLinear", "dispatchTouchEvent-" + getS(ev.getAction()));
24         if (isDispatch) {
25             boolean one = super.dispatchTouchEvent(ev);
26             Log.e("David--MyLinear", "dispatchTouchEvent() super.dispatchTouchEvent(event) = " + one);
27             return one;
28         } else {
29             return true;
30         }
31     }
32 
33     @Override
34     public boolean onTouchEvent(MotionEvent event) {
35         Log.e("David--MyLinear", "onTouchEvent-" + getS(event.getAction()));
36         return true;
37     }
38 
39     public static String getS(int action) {
40         String ret = "null";
41         switch (action) {
42             case MotionEvent.ACTION_CANCEL:
43                 ret = "Cancel";
44                 break;
45             case MotionEvent.ACTION_DOWN:
46                 ret = "Down";
47                 break;
48             case MotionEvent.ACTION_MOVE:
49                 ret = "Move";
50                 break;
51             case MotionEvent.ACTION_UP:
52                 ret = "Up";
53                 break;
54         }
55         return ret;
56     }
57 }

继承自LinearLayout,重写了那几个方法。

CustomView.java

 1 public class CustomView extends View {
 2     private Paint mPaint;
 3 
 4     public CustomView(Context context) {
 5         super(context);
 6         mPaint = new Paint();
 7         mPaint.setColor(Color.RED);
 8     }
 9 
10     public CustomView(Context context, AttributeSet attrs) {
11         super(context, attrs);
12         mPaint = new Paint();
13         mPaint.setColor(Color.RED);
14     }
15 
16     public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
17         super(context, attrs, defStyleAttr);
18         mPaint = new Paint();
19         mPaint.setColor(Color.RED);
20     }
21 
22     @Override
23     public void draw(Canvas canvas) {
24         super.draw(canvas);
25         //canvas.drawLine(0, 0, 100, 400, mPaint);
26         canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
27         mPaint.setColor(Color.GREEN);
28         canvas.drawText("我是文字", 30, 45, mPaint);
29         canvas.restore();
30     }
31 
32     @Override
33     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
34         //super.onMeasure(widthMeasureSpec, heightMeasureSpec);
35         setMeasuredDimension(getContext().getResources().getDisplayMetrics().widthPixels, 400);
36     }
37 
38     @Override
39     public boolean dispatchTouchEvent(MotionEvent event) {
40         Log.e("David--C---------------", "dispatchTouchEvent-" + MyLinear1.getS(event.getAction()));
41         boolean one = super.dispatchTouchEvent(event);
42         Log.e("David--C---------------", "dispatchTouchEvent() super.dispatchTouchEvent(event) = " + one);
43         return one;
44     }
45 
46     @Override
47     public boolean onTouchEvent(MotionEvent event) {
48         Log.e("David--C---------------", "onTouchEvent-" + MyLinear1.getS(event.getAction()));
49         return false;
50     }
51 }

这是一个简单地View。

布局文件:当前页面的根View可以认为是MyLinear1,里面只包含了一个子CustomView。至于Activity的根View,我们后续再分析。

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <com.meituan.david.myapplication.MyLinear1 xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:id="@+id/linear"
 4     android:layout_width="match_parent"
 5     android:layout_height="wrap_content"
 6     android:orientation="vertical">
 7     <com.meituan.david.myapplication.CustomView
 8         android:layout_width="match_parent"
 9         android:layout_height="200dp" />
10 </com.meituan.david.myapplication.MyLinear1>

上日志:

前提:MyLinear1的onInterceptTouchEvent()方法返回true,意味着Touch事件不会向Children View传递,所以CustomView的两个方法的返回值无所谓了~

1. MyLinear1的dispatchTouchEvent()方法返回false,onTouchEvent返回false,日志如下:
1 11-30 18:12:01.014 E/David--MyLinear(20933): dispatchTouchEvent-Down
2 11-30 18:12:02.139 E/David--MyLinear(20933): dispatchTouchEvent-Down
3 11-30 18:12:03.004 E/David--MyLinear(20933): dispatchTouchEvent-Down

触摸(点击、滑动)操作三次打出的log,事件根本没有传递到子View,并且,由于自己MyLinear1不想处理Touch事件,所以只收到了ACTION_DOWN,此后的Touch事件不会再传入了;

2. MyLinear1的dispatchTouchEvent()方法返回false,onTouchEvent返回true,日志如下:

日志和1是一样的,很明显,如果MyLinear1的dispatchTouchEvent()直接返回false,无论其他方法的返回值如何,说明当前ViewGroup不想处理Touch事件,所有后续的Touch事件不会再传入了。

3. MyLinear1的dispatchTouchEvent()方法返回true,onTouchEvent返回false,日志如下:

11-30 18:26:38.451 E/David--MyLinear( 8268): dispatchTouchEvent-Down
11-30 18:26:38.592 E/David--MyLinear( 8268): dispatchTouchEvent-Move
11-30 18:26:38.608 E/David--MyLinear( 8268): dispatchTouchEvent-Move
11-30 18:26:38.625 E/David--MyLinear( 8268): dispatchTouchEvent-Move
11-30 18:26:38.798 E/David--MyLinear( 8268): dispatchTouchEvent-Up

MyLinear1的dispatchTouchEvent()直接返回true,Touch事件源源不断的传入,但是onInterceptTouchEvent()和onTouchEvent()并没有被调用。

根据以上三点,我们可以得出一个结论:

MyLinear1的dispatchTouchEvent()方法,会接受"系统"(暂且认为是系统吧)传入的Touch事件,如果直接返回false,则系统不会再传入Touch事件;返回true,会继续传入。但是,由于没有调用super.dispatchTouchEvent()方法,此Touch事件既不会被拦截/分发,也不会被处理(onInterceptTouchEvent()和onTouchEvent()不会被调用)~

4. MyLinear1的dispatchTouchEvent()方法返回super.dispatchTouchEvent(),onTouchEvent返回false,日志如下:

1 11-30 18:56:04.757 E/David--MyLinear( 8156): dispatchTouchEvent-Down
2 11-30 18:56:04.758 E/David--MyLinear( 8156): onInterceptTouchEvent-Down
3 11-30 18:56:04.758 E/David--MyLinear( 8156): onTouchEvent-Down
4 11-30 18:56:04.758 E/David--MyLinear( 8156): dispatchTouchEvent() super.dispatchTouchEvent(event) = false

另外两个方法得到了调用,但是由于onTouchEvent返回false,说明MyLinear1不想处理后续的Touch事件,所以后续的Touch事件没有传入。仔细看,此时的super.dispatchTouchEvent(event) = false,这也解释了为什么只传入了ACTION_DOWN事件。

5. MyLinear1的dispatchTouchEvent()方法返回super.dispatchTouchEvent(),onTouchEvent返回true,日志如下:

 1 11-30 19:02:18.189 E/David--MyLinear(11377): dispatchTouchEvent-Down
 2 11-30 19:02:18.189 E/David--MyLinear(11377): onInterceptTouchEvent-Down
 3 11-30 19:02:18.189 E/David--MyLinear(11377): onTouchEvent-Down
 4 11-30 19:02:18.476 E/David--MyLinear(11377): dispatchTouchEvent() super.dispatchTouchEvent(event)-true
 5 
 6 11-30 19:02:18.505 E/David--MyLinear(11377): dispatchTouchEvent-Move
 7 11-30 19:02:18.505 E/David--MyLinear(11377): onTouchEvent-Move
 8 11-30 19:02:18.505 E/David--MyLinear(11377): dispatchTouchEvent() super.dispatchTouchEvent(event)-true
 9 
10 11-30 19:02:18.505 E/David--MyLinear(11377): dispatchTouchEvent-Up
11 11-30 19:02:18.505 E/David--MyLinear(11377): onTouchEvent-Up
12 11-30 19:02:18.505 E/David--MyLinear(11377): dispatchTouchEvent() super.dispatchTouchEvent(event)-true

现在onTouchEvent返回true,说明他愿意处理Touch事件,所以就源源不断的传入了,同时super.dispatchTouchEvent(event)=true,接受系统传入的Touch事件~同时呢,如果当前View决定处理=Touch事件,那么后续传入的Touch事件,就不需要onInterceptTouchEvent了....

根据4、5,我们可以得出一个结论:

dispatchTouchEvent()的返回值和onTouchEvent()的返回值是息息相关的,所以diapatchTouchEvent方法不调用super.dispatchTouchEvent()是没有任何意义的。

以上都是在onInterceptTouchEvent()方法返回true的情况下做的测试,Touch事件根本就没有传递到Children View,那么我们看看onInterceptTouchEvent()方法返回false时,会有什么样的结果。

前提:MyLinear1的dispatchTouchEvent()方法返回super.dispatchTouchEvent(),onInterceptTouchEvent()方法返回false,表示不拦截Touch事件

1. CustomView的dispatchTouchEvent()返回super.dispatchTouchEvent,onTouchEvent返回false,MyLinear1的onTouchEvent返回true,日志如下: 

 1 11-30 19:19:54.430 E/David--MyLinear( 1860): dispatchTouchEvent-Down
 2 11-30 19:19:54.430 E/David--MyLinear( 1860): onInterceptTouchEvent-Down
 3 11-30 19:19:54.430 E/David--C---------------( 1860): dispatchTouchEvent-Down
 4 11-30 19:19:54.430 E/David--C---------------( 1860): onTouchEvent-Down
 5 11-30 19:19:54.430 E/David--C---------------( 1860): dispatchTouchEvent() super.dispatchTouchEvent(event) = false
 6 11-30 19:19:54.430 E/David--MyLinear( 1860): onTouchEvent-Down
 7 
 8 11-30 19:19:54.659 E/David--MyLinear( 1860): dispatchTouchEvent() super.dispatchTouchEvent(event) = true
 9 11-30 19:19:54.680 E/David--MyLinear( 1860): dispatchTouchEvent-Move
10 11-30 19:19:54.680 E/David--MyLinear( 1860): onTouchEvent-Move
11 11-30 19:19:54.680 E/David--MyLinear( 1860): dispatchTouchEvent() super.dispatchTouchEvent(event) = true
12 11-30 19:19:54.794 E/David--MyLinear( 1860): dispatchTouchEvent-Move
13 11-30 19:19:54.794 E/David--MyLinear( 1860): onTouchEvent-Move
14 
15 11-30 19:19:54.794 E/David--MyLinear( 1860): dispatchTouchEvent() super.dispatchTouchEvent(event) = true
16 11-30 19:19:54.825 E/David--MyLinear( 1860): dispatchTouchEvent-Up
17 11-30 19:19:54.825 E/David--MyLinear( 1860): onTouchEvent-Up
18 11-30 19:19:54.825 E/David--MyLinear( 1860): dispatchTouchEvent() super.dispatchTouchEvent(event) = true

 事件顺利的传到Custom View,但是由于CustomView的onTouchEvent返回false,意味着他不想处理后续Touch事件,所以,CustomView的dispatchTouchEvent()返回false,Touch事件不再传入到CustomView。那么传入的ACTION_DOWN和后续的Touch事件怎么办呢,总不能没人管吧?其实,当Child View不想处理ACTION_DOWN事件时,ACTION_DOWN就会被回传至Father View,很显然,CustomView的父View就是MyLinear1了,所以MyLinear1的onTouchEvent收到了该事件。如果MyLinear的onTouchEvent决定处理Touch事件,那么就会消化ACTION_DOWN事件,同时后续的Touch事件会源源不断的传给MyLienar1,与CustomView就没有半毛钱关系了~日志也是这么显示的。

2. 和1一样的条件,如果MyLinear1不愿意处理Touch事件呢?onTouchEvent()返回false,我们看日志:

1 11-30 19:28:42.199 E/David--MyLinear(11273): dispatchTouchEvent-Down
2 11-30 19:28:42.199 E/David--MyLinear(11273): onInterceptTouchEvent-Down
3 11-30 19:28:42.199 E/David--C---------------(11273): dispatchTouchEvent-Down
4 11-30 19:28:42.199 E/David--C---------------(11273): onTouchEvent-Down
5 11-30 19:28:42.199 E/David--C---------------(11273): dispatchTouchEvent() super.dispatchTouchEvent(event) = false
6 11-30 19:28:42.199 E/David--MyLinear(11273): onTouchEvent-Down
7 11-30 19:28:42.199 E/David--MyLinear(11273): dispatchTouchEvent() super.dispatchTouchEvent(event) = false

CustomView不愿处理的Touch事件被回传至MyLinear1,这没问题,但是MyLinear1也不愿意处理Touch事件,所以onTouchEvent返回false,同时super.dispatchTouchEvent(event)返回false,后续的Touch事件就不会再传入了~

3. CustomView的dispatchTouchEvent()返回super.dispatchTouchEvent,onTouchEvent返回true,MyLinear1的onTouchEvent返回false,日志如下:

 1 12-01 12:00:30.735 E/David--MyLinear(  363): dispatchTouchEvent-Down
 2 12-01 12:00:30.735 E/David--MyLinear(  363): onInterceptTouchEvent-Down
 3 12-01 12:00:30.735 E/David--C---------------(  363): dispatchTouchEvent-Down
 4 12-01 12:00:30.735 E/David--C---------------(  363): onTouchEvent-Down
 5 12-01 12:00:30.735 E/David--C---------------(  363): dispatchTouchEvent() super.dispatchTouchEvent(event) = true
 6 12-01 12:00:30.735 E/David--MyLinear(  363): dispatchTouchEvent() super.dispatchTouchEvent(event) = true
 7 
 8 12-01 12:00:30.958 E/David--MyLinear(  363): dispatchTouchEvent-Move
 9 12-01 12:00:30.958 E/David--MyLinear(  363): onInterceptTouchEvent-Move
10 12-01 12:00:30.958 E/David--C---------------(  363): dispatchTouchEvent-Move
11 12-01 12:00:30.958 E/David--C---------------(  363): onTouchEvent-Move
12 12-01 12:00:30.958 E/David--C---------------(  363): dispatchTouchEvent() super.dispatchTouchEvent(event) = true
13 12-01 12:00:30.958 E/David--MyLinear(  363): dispatchTouchEvent() super.dispatchTouchEvent(event) = true
14 
15 12-01 12:00:30.974 E/David--MyLinear(  363): dispatchTouchEvent-Move
16 12-01 12:00:30.974 E/David--MyLinear(  363): onInterceptTouchEvent-Move
17 12-01 12:00:30.974 E/David--C---------------(  363): dispatchTouchEvent-Move
18 12-01 12:00:30.974 E/David--C---------------(  363): onTouchEvent-Move
19 12-01 12:00:30.974 E/David--C---------------(  363): dispatchTouchEvent() super.dispatchTouchEvent(event) = true
20 12-01 12:00:30.974 E/David--MyLinear(  363): dispatchTouchEvent() super.dispatchTouchEvent(event) = true
21 
22 12-01 12:00:30.992 E/David--MyLinear(  363): dispatchTouchEvent-Move
23 12-01 12:00:30.992 E/David--MyLinear(  363): onInterceptTouchEvent-Move
24 12-01 12:00:30.992 E/David--C---------------(  363): dispatchTouchEvent-Move
25 12-01 12:00:30.992 E/David--C---------------(  363): onTouchEvent-Move
26 12-01 12:00:30.992 E/David--C---------------(  363): dispatchTouchEvent() super.dispatchTouchEvent(event) = true
27 12-01 12:00:30.992 E/David--MyLinear(  363): dispatchTouchEvent() super.dispatchTouchEvent(event) = true
28 
29 12-01 12:00:31.026 E/David--MyLinear(  363): dispatchTouchEvent-Up
30 12-01 12:00:31.026 E/David--MyLinear(  363): onInterceptTouchEvent-Up
31 12-01 12:00:31.026 E/David--C---------------(  363): dispatchTouchEvent-Up
32 12-01 12:00:31.026 E/David--C---------------(  363): onTouchEvent-Up
33 12-01 12:00:31.026 E/David--C---------------(  363): dispatchTouchEvent() super.dispatchTouchEvent(event) = true
34 12-01 12:00:31.026 E/David--MyLinear(  363): dispatchTouchEvent() super.dispatchTouchEvent(event) = true

CustomView收到Touch事件,而且想处理它,onTouchEvent返回true,此时Touch事件会源源不断地传至CustomView,CustomView和MyLinear1的dispatchTouchEvent都返回true~有两个新特点:

1、MyLinear1的onInterceptTouchEvent每次都会被调用,这说明,如果ViewGroup决定自己处理Touch事件,那么ACTION_DOWN之后的事件都不会经过onInterceptTouchEvent。但是Children View就不一样了,每次都得被拦截一下,谁让你是Children呢~

2、一旦Children View开始处理Touch事件,就没有MyLinear1的onTouchEvent什么事了。

本文总结:

    经过上面的分析,应该是说清楚了dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()方法的使用情况了,记住,这三个方法的调用是有顺序的。对于简单的自定义View或者ViewGroup的需求,应该已经能满足需求了。毕竟,最好是不要直接完全重写dispatchTouchEvent或者onTouchEvent,可以适当的加一些判断,但是最终还是要调用View的方法的,这已经能满足绝大多数的需求了。下一篇,咱们仔细研究一下dispatchTouchEvent()和onTouchEvent()方法的代码

 posted on 2015-11-30 10:22  wlrhnh  阅读(1674)  评论(0编辑  收藏  举报