前言
在自己定义ViewGroup中。有时候须要实现触摸事件拦截。比方ListView下拉刷新就是典型的触摸事件拦截的样例。
触摸事件拦截就是在触摸事件被parent view拦截,而不会分发给其child。即使触摸发生在该child身上。被拦截的事件会转到parent view的onTouchEvent方法中进行处理。
可是这个交互过程还是挺复杂的,有多种情况,今天我们就来分析一下吧。这篇分析文章已经放了一段时间了,假设有不论什么问题请高人指出。
触摸事件的分发
比方一个LinearLayout中又一个TextView。当触摸这个TextView时触摸事件会先打到LinearLayout。然后再到达TextView。假设LinearLayout将触摸事件拦截了。那么TextView就会收到一个CANCEL事件,其它触摸就收不到了。可是触摸事件的处理过程是一个冒泡事件。还是以上面的TextView为例,正常情况下,事件从上到下分发到TextView上,TextView则会对该事件进行处理,假设TextView处理了该事件,即TextView的dispatchTouchEvent返回了true, 那么该事件就被消费了。可是假设TextView的dispatchTouchEvent返回的是false, 则代表这个事件没有被处理,此时该事件就会从下到上(即从child 到 view group的过程)找parent view进行处理。
假设parent view也没有处理。那么终于会交给Activity (假设是Activity窗体) 的onTouchEvent来处理。以下就是ViewGroup的事件分发过程。更具体的资料请參考Android Touch事件分发过程。
触摸事件的拦截
ViewGroup对于事件的拦截是一个复杂的流程,假设你想对触摸事件进行拦截,那么你须要覆写onInterceptTouchEvent方法,而且返回true。然后兴许的事件就会被转移到该ViewGroup的onTouchEvent方法进行处理。而在兴许的事件处理过程中onInterceptTouchEvent中也不会收到兴许事件,因此你也须要覆写onTouchEvent方法。
我们首先看看onInterceptTouchEvent方法的官方说明 :
public boolean onInterceptTouchEvent (MotionEvent ev)
Implement this method to intercept all touch screen motion events. This allows you to watch events as they are dispatched to your children, and take ownership of the current gesture at any point.
Using this function takes some care, as it has a fairly complicated interaction with View.onTouchEvent(MotionEvent), and using it requires implementing that method as well as this one in the correct way. Events will be received in the following order:
You will receive the down event here.
The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle; this means you should implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it). Also, by returning true from onTouchEvent(), you will not receive any following events in onInterceptTouchEvent() and all touch processing must happen in onTouchEvent() like normal.
For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and then to the target's onTouchEvent().
If you return true from here, you will not receive any following events: the target view will receive the same event but with the action ACTION_CANCEL, and all further events will be delivered to your onTouchEvent() method and no longer appear here.
翻译例如以下 :
实现这种方法来拦截全部触摸事件。这会使得您能够监控到全部分发到你的子视图的事件。然后您能够随时控制当前的手势。
使用这种方法您须要花些精力,由于它与View.onTouchEvent(MotionEvent)的交互很复杂,而且要想使用这个功能还须要把当前ViewGroup的onTouchEvent方法和子控件的onTouchEvent方法正确地结合在一起使用。
事件获取顺序例如以下:
你将从这里開始接收ACTION_DOWN触摸事件。
ACTION_DOWN触摸事件能够由该ViewGroup自己处理,也能够由它的子控件的onTouchEvent进行处理。这就意味着你须要实现onTouchEvent(MotionEvent)方法而且返回true,这样你才干够接收到兴许的事件(以免会继续寻找父控件进行处理)。
假设你在onTouchEvent(MotionEvent)返回了true,那么在onInterceptTouchEvent()方法中您将不会再收到兴许的事件,全部这些兴许的事件(比如您在ACTION_DOWN中返回了true,那么ACTION_MOVE, ACTION_UP这些成为兴许事件)将会被本类的onTouchEvent(MotionEvent)方法中被处理。
************
仅仅要您在onInterceptTouchEvent方法中返回false,每一个兴许的事件(从当前事件到最后ACTION_UP事件)将会先分发到onInterceptTouchEvent中。然后再交给目标子控件的onTouchEvent处理 (前提是子控件的onTouchEvent返回是true )。
假设在onInterceptTouchEvent返回true,onInterceptTouchEvent方法中将不会收到兴许的不论什么事件,目标子控件中除了ACTION_CANCEL外也不会接收全部这些兴许事件。全部的兴许事件将会被交付到你自己的onTouchEvent()方法中。
************
触摸事件拦截演示样例
import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.FrameLayout;
import android.widget.Scroller; public class TouchLayout extends FrameLayout { private String TAG = TouchLayout.class.getSimpleName(); public TouchLayout(Context context) {
super(context); } public TouchLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public TouchLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// setClickable(true);
} @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean result = super.dispatchTouchEvent(ev) ; return result;
} @Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//
final int action = MotionEventCompat.getActionMasked(ev);
// Always handle the case of the touch gesture being complete.
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
// Do not intercept touch event, let the child handle it
return false;
} TouchUtils.showEventInfo(TAG + "# onInterceptTouchEvent", action); return false;
} @Override
public boolean onTouchEvent(MotionEvent ev) {
TouchUtils.showEventInfo(TAG + "# *** onTouchEvent", ev.getAction());
Log.d(TAG, "### is Clickable = " + isClickable());
return super.onTouchEvent(ev);
// return true;
} }
TouchTv ( View 类型)
public class TouchTv extends TextView { private String TAG = TouchTv.class.getSimpleName(); public TouchTv(Context context) {
this(context, null);
} public TouchTv(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public TouchTv(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// setClickable(true);
} @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
TouchUtils.showEventInfo(TAG + "#dispatchTouchEvent", ev.getAction());
boolean result = super.dispatchTouchEvent(ev);
Log.d(TAG, "### dispatchTouchEvent result = " + result);
return result;
} @Override
public boolean onTouchEvent(MotionEvent ev) {
TouchUtils.showEventInfo(TAG + "#onTouchEvent", ev.getAction());
boolean result = super.onTouchEvent(ev);
Log.d(TAG, "### onTouchEvent result = " + result);
return result;
}
}
Activity :
public class MainActivity extends Activity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); setContentView(R.layout.touch_event_intercept); View myView = findViewById(R.id.my_button); ValueAnimator colorAnim = ObjectAnimator.ofInt(myView,
"backgroundColor", /* Red */
0xFFFF8080, /* Blue */0xFF8080FF);
colorAnim.setDuration(3000);
colorAnim.setEvaluator(new ArgbEvaluator());
colorAnim.setRepeatCount(ValueAnimator.INFINITE);
colorAnim.setRepeatMode(ValueAnimator.REVERSE);
colorAnim.start(); ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(myView, "scaleX",
0.5f);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatMode(ObjectAnimator.REVERSE);
objectAnimator.start(); Log.d("", "### Activiti中getWindow()获取的类型是 : " + this.getWindow()); // state list
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(new int[] {
android.R.attr.state_enabled
}, getResources().getDrawable(R.drawable.ic_launcher));
stateListDrawable.addState(new int[] {
android.R.attr.state_pressed
}, getResources().getDrawable(R.drawable.ic_launcher)); } @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// Log.d("", "### activity dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
} @Override
public boolean onTouchEvent(MotionEvent event) {
TouchUtils.showEventInfo("activity onTouchEvent", event.getAction());
return super.onTouchEvent(event);
} }
touch_event_intercept.xml :
<myview.TouchLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
tools:context="com.example.touch_event.MainActivity"
tools:ignore="MergeRootFrame" > <myview.TouchTv
android:id="@+id/my_button"
android:layout_width="match_parent"
android:layout_height="160dp"
android:layout_gravity="center"
android:layout_margin="20dp"
android:background="#00aa00"
android:gravity="center"
android:text="@string/hello_world"
android:textSize="30sp" /> </myview.TouchLayout>
情景分析
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYmJveWZlaXl1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" width="400" height="640" />
情景1
// 事件拦截
10-01 20:22:52.892: D/TouchLayout# onInterceptTouchEvent(407): ### action --> ACTION_DOWN
// 处理
10-01 20:22:52.892: D/TouchLayout# *** onTouchEvent(407): ### action --> ACTION_DOWN
// DOWN的兴许事件不经过onInterceptTouchEvent。直接交给的onTouchEvent处理
10-01 20:22:52.917: D/TouchLayout# *** onTouchEvent(407): ### action --> ACTION_MOVE
10-01 20:22:52.937: D/TouchLayout# *** onTouchEvent(407): ### action --> ACTION_MOVE
10-01 20:22:52.957: D/TouchLayout# *** onTouchEvent(407): ### action --> ACTION_MOVE
10-01 20:22:52.977: D/TouchLayout# *** onTouchEvent(407): ### action --> ACTION_MOVE
10-01 20:22:52.977: D/TouchLayout# *** onTouchEvent(407): ### action --> ACTION_MOVE
10-01 20:22:52.997: D/TouchLayout# *** onTouchEvent(407): ### action --> ACTION_MOVE
10-01 20:22:53.017: D/TouchLayout# *** onTouchEvent(407): ### action --> ACTION_MOVE
10-01 20:22:53.017: D/TouchLayout# *** onTouchEvent(407): ### action --> ACTION_UP
情景2
// DOWN中没有对事件进行拦截,因此能够被TouchTv进行处理
10-01 20:32:05.017: D/TouchLayout# onInterceptTouchEvent(573): ### action --> ACTION_DOWN
// TouchTv事件分发
10-01 20:32:05.017: D/TouchTv#dispatchTouchEvent(573): ### action --> ACTION_DOWN
// TouchTv对事件进行处理,TouchTv的onTouchEvent返回false,导致事件交给TouchLayout的onTouchEvent处理
10-01 20:32:05.017: D/TouchTv#onTouchEvent(573): ### action --> ACTION_DOWN
// TouchLayout的onTouchEvent处理DOWN事件
10-01 20:32:05.017: D/TouchLayout# *** onTouchEvent(573): ### action --> ACTION_DOWN
// TouchLayout的onTouchEvent处理兴许事件
10-01 20:32:05.062: D/TouchLayout# *** onTouchEvent(573): ### action --> ACTION_MOVE
10-01 20:32:05.082: D/TouchLayout# *** onTouchEvent(573): ### action --> ACTION_MOVE
10-01 20:32:05.267: D/TouchLayout# *** onTouchEvent(573): ### action --> ACTION_MOVE
10-01 20:32:05.287: D/TouchLayout# *** onTouchEvent(573): ### action --> ACTION_MOVE
10-01 20:32:05.307: D/TouchLayout# *** onTouchEvent(573): ### action --> ACTION_MOVE
10-01 20:32:05.307: D/TouchLayout# *** onTouchEvent(573): ### action --> ACTION_MOVE
10-01 20:32:05.312: D/TouchLayout# *** onTouchEvent(573): ### action --> ACTION_UP
情景3
onTouchEvent返回false,表明事件没有被消费,须要交给parent处理,假设终于该事件没有被处理。那么事件交给Activity的onTouchEvent处理。
// 事件拦截onInterceptTouchEvent
10-01 20:16:03.617: D/TouchLayout# onInterceptTouchEvent(32675): ### action --> ACTION_DOWN
// 事件处理onTouchEvent
10-01 20:16:03.617: D/TouchLayout# *** onTouchEvent(32675): ### action --> ACTION_DOWN
// TouchLayout的dispatchTouchEvent终于返回了false,
10-01 20:16:03.617: D/TouchLayout(32675): ### dispatchTouchEvent, return false
// 事件没有被处理。终于交给了Activity的onTouchEvent处理
10-01 20:16:03.617: D/activity onTouchEvent(32675): ### action --> ACTION_DOWN
10-01 20:16:03.697: D/activity onTouchEvent(32675): ### action --> ACTION_MOVE
10-01 20:16:03.712: D/activity onTouchEvent(32675): ### action --> ACTION_MOVE
10-01 20:16:03.732: D/activity onTouchEvent(32675): ### action --> ACTION_MOVE
10-01 20:16:03.882: D/activity onTouchEvent(32675): ### action --> ACTION_MOVE
10-01 20:16:03.897: D/activity onTouchEvent(32675): ### action --> ACTION_MOVE
10-01 20:16:03.917: D/activity onTouchEvent(32675): ### action --> ACTION_UP
情景4
// TouchLayout不正确事件进行拦截
10-01 20:43:04.682: D/TouchLayout# onInterceptTouchEvent(814): ### action --> ACTION_DOWN
// 事件被TouchTv分发
10-01 20:43:04.682: D/TouchTv#dispatchTouchEvent(814): ### action --> ACTION_DOWN
// 事件被TouchTv处理
10-01 20:43:04.682: D/TouchTv#onTouchEvent(814): ### action --> ACTION_DOWN
// 事件被TouchTv的处理结果为false,因此该事件须要找parent来处理
10-01 20:43:04.682: D/TouchTv(814): ### dispatchTouchEvent result = false
// 事件被交给TouchTv的parent的onTouchEvent处理,即TouchLayout的onTouchEvent,该方法返回true
// 因此兴许事件继续交给TouchLayout处理
10-01 20:43:04.682: D/TouchLayout# *** onTouchEvent(814): ### action --> ACTION_DOWN
10-01 20:43:04.727: D/TouchLayout# *** onTouchEvent(814): ### action --> ACTION_MOVE
10-01 20:43:04.747: D/TouchLayout# *** onTouchEvent(814): ### action --> ACTION_MOVE
10-01 20:43:04.872: D/TouchLayout# *** onTouchEvent(814): ### action --> ACTION_MOVE
10-01 20:43:04.892: D/TouchLayout# *** onTouchEvent(814): ### action --> ACTION_MOVE
10-01 20:43:04.892: D/TouchLayout# *** onTouchEvent(814): ### action --> ACTION_UP
情景5
// TouchLayout不拦截事件,因此事件分发给TouchTv进行处理。而TouchTv的处理结果为true,因此兴许的事件将会先从
// TouchLayout的onInterceptTouchEvent再到TouchTv的onTouchEvent
10-01 20:48:49.612: D/TouchLayout# onInterceptTouchEvent(1030): ### action --> ACTION_DOWN
// TouchTv处理事件
10-01 20:48:49.612: D/TouchTv#dispatchTouchEvent(1030): ### action --> ACTION_DOWN
10-01 20:48:49.612: D/TouchTv#onTouchEvent(1030): ### action --> ACTION_DOWN
10-01 20:48:49.612: D/TouchTv(1030): ### dispatchTouchEvent result = true
// 兴许事件从TouchLayout的onInterceptTouchEvent再到TouchTv的onTouchEvent
10-01 20:48:49.697: D/TouchLayout# onInterceptTouchEvent(1030): ### action --> ACTION_MOVE
10-01 20:48:49.697: D/TouchTv#dispatchTouchEvent(1030): ### action --> ACTION_MOVE
10-01 20:48:49.697: D/TouchTv#onTouchEvent(1030): ### action --> ACTION_MOVE
10-01 20:48:49.697: D/TouchTv(1030): ### dispatchTouchEvent result = true
10-01 20:48:49.717: D/TouchLayout# onInterceptTouchEvent(1030): ### action --> ACTION_MOVE
10-01 20:48:49.717: D/TouchTv#dispatchTouchEvent(1030): ### action --> ACTION_MOVE
10-01 20:48:49.717: D/TouchTv#onTouchEvent(1030): ### action --> ACTION_MOVE
10-01 20:48:49.717: D/TouchTv(1030): ### dispatchTouchEvent result = true
// UP事件直接在TouchTv中进行分发
10-01 20:48:49.782: D/TouchTv#dispatchTouchEvent(1030): ### action --> ACTION_UP
10-01 20:48:49.782: D/TouchTv#onTouchEvent(1030): ### action --> ACTION_UP
10-01 20:48:49.782: D/TouchTv(1030): ### dispatchTouchEvent result = true
情景6
// TouchLayout不正确DOWN进行拦截
10-01 20:56:37.642: D/TouchLayout# onInterceptTouchEvent(1205): ### action --> ACTION_DOWN
// TouchTv分发与处理DOWN事件
10-01 20:56:37.642: D/TouchTv#dispatchTouchEvent(1205): ### action --> ACTION_DOWN
10-01 20:56:37.642: D/TouchTv#onTouchEvent(1205): ### action --> ACTION_DOWN
10-01 20:56:37.642: D/TouchTv(1205): ### dispatchTouchEvent result = true
// TouchLayout对MOVE事件进行拦截
10-01 20:56:37.712: D/TouchLayout# onInterceptTouchEvent(1205): ### action --> ACTION_MOVE
// TouchTv收到一个CANCEL事件,然后不会不到MOVE以及兴许的事件
10-01 20:56:37.712: D/TouchTv#dispatchTouchEvent(1205): ### action --> ACTION_CANCEL
10-01 20:56:37.712: D/TouchTv#onTouchEvent(1205): ### action --> ACTION_CANCEL
10-01 20:56:37.712: D/TouchTv(1205): ### dispatchTouchEvent result = true
// MOVE以及兴许事件被TouchLayout处理
10-01 20:56:37.727: D/TouchLayout# *** onTouchEvent(1205): ### action --> ACTION_MOVE
10-01 20:56:37.747: D/TouchLayout# *** onTouchEvent(1205): ### action --> ACTION_MOVE
10-01 20:56:37.762: D/TouchLayout# *** onTouchEvent(1205): ### action --> ACTION_MOVE
10-01 20:56:37.777: D/TouchLayout# *** onTouchEvent(1205): ### action --> ACTION_MOVE
10-01 20:56:37.797: D/TouchLayout# *** onTouchEvent(1205): ### action --> ACTION_MOVE
10-01 20:56:37.997: D/TouchLayout# *** onTouchEvent(1205): ### action --> ACTION_MOVE
10-01 20:56:38.012: D/TouchLayout# *** onTouchEvent(1205): ### action --> ACTION_MOVE
10-01 20:56:38.017: D/TouchLayout# *** onTouchEvent(1205): ### action --> ACTION_UP
总结
以上的几种情况就是我们常常遇到的了。总结起来有几个重要的点 :
比方情景6其中。在TouchLayout的DOWN时不正确事件进行拦截,这时事件会被TouchTv正常处理。
可是在MOVE时事件被拦截了,此时TouchTv收到了一个CANCEL事件。MOVE以及兴许的事件就交给了TouchLayout进行处理。这个情景就是我们做下拉刷新时要用的场景了。
/**
* {@inheritDoc}
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!onFilterTouchEventForSecurity(ev)) {
return false;
} final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
final Rect frame = mTempRect;
// 是否禁用拦截,假设为true表示不能拦截事件;反之。则为能够拦截事件
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
// ACTION_DOWN事件,即按下事件
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
mMotionTarget = null;
}
// If we're disallowing intercept or if we're allowing and we didn't
// intercept。假设不同意事件拦截或者不拦截该事件,那么运行以下的操作
if (disallowIntercept || !onInterceptTouchEvent(ev)) // 1、是否禁用拦截、是否拦截事件的推断
// reset this event's action (just to protect ourselves)
ev.setAction(MotionEvent.ACTION_DOWN);
// We know we want to dispatch the event down, find a child
// who can handle it, start with the front-most child.
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount; for (int i = count - 1; i >= 0; i--) // 2、迭代全部子view,查找触摸事件在哪个子view的坐标范围内
final View child = children[i];
// 该child是可见的
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
// 3、获取child的坐标范围
child.getHitRect(frame);
// 4、推断发生该事件坐标是否在该child坐标范围内
if (frame.contains(scrolledXInt, scrolledYInt))
// offset the event to the view's coordinate system
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
// 5、child处理该事件,假设返回true,那么mMotionTarget为该child。正常情况下,
// dispatchTouchEvent(ev)的返回值即onTouchEcent的返回值。因此onTouchEcent假设返回为true,
// 那么mMotionTarget为触摸事件所在位置的child。
if (child.dispatchTouchEvent(ev))
// 6、 mMotionTarget为该child
mMotionTarget = child;
return true;
} }
}
}
}
}// end if boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL); if (isUpOrCancel) {
// Note, we've already copied the previous state to our local
// variable, so this takes effect on the next event
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
} // 触摸事件的目标view, 即触摸所在的view
final View target = mMotionTarget;
// 7、假设mMotionTarget为空。那么运行super.super.dispatchTouchEvent(ev),
// 即View.dispatchTouchEvent(ev),就是该View Group自己处理该touch事件,仅仅是又走了一遍View的分发过程而已.
// 拦截事件或者在不拦截事件且target view的onTouchEvent返回false的情况都会运行到这一步.
if (target == null) {
// We don't have a target, this means we're handling the
// event as a regular view.
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
return super.dispatchTouchEvent(ev);
} // 8、假设没有禁用事件拦截,而且onInterceptTouchEvent(ev)返回为true。即进行事件拦截. ( 在某个事件时拦截的情形 )
if (!disallowIntercept && onInterceptTouchEvent(ev)) {
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
ev.setAction(MotionEvent.ACTION_CANCEL);
ev.setLocation(xc, yc);
//
if (!target.dispatchTouchEvent(ev)) {
// target didn't handle ACTION_CANCEL. not much we can do
// but they should have.
}
// clear the target
mMotionTarget = null;
// Don't dispatch this event to our own view, because we already
// saw it when intercepting; we just want to give the following
// event to the normal onTouchEvent().
return true;
} if (isUpOrCancel) {
mMotionTarget = null;
} // finally offset the event to the target's coordinate system and
// dispatch the event.
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
ev.setLocation(xc, yc); if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
mMotionTarget = null;
}
// 9、事件不拦截,且target view在ACTION_DOWN时返回true。那么兴许事件由target来处理事件
return target.dispatchTouchEvent(ev);
}
假设不正确事件进来拦截,且TouchTv对事件的处理返回true。那么在DOWN事件时,mMotionTarget就是TouchTv,兴许的事件就会通过凝视9来处理,即直接交给TouvhTv来处理。假设在DOWN时就拦截事件。那么mMotionTarget为空,则会运行凝视7出的代码,一直调用super.dispatchTouchEvent处理事件。即调用本类的事件处理,终于会调用onTouchEvent方法。假设在DOWN时不拦截。MOVE时拦截,那么会引发凝视8的代码,target view收到一个cancel事件。且mMotionTarget被置空,兴许事件在凝视7出的代理进行处理,即在自己的onTouchEvent中进行处理。