轉載地址 http://blog.csdn.net/bboyfeiyu/article/details/39719543
前言
在自定義ViewGroup中,有時候需要實現觸摸事件攔截,比如ListView下拉刷新就是典型的觸摸事件攔截的例子。觸摸事件攔截就是在觸摸事件被parent view攔截,而不會分發給其child,即使觸摸發生在該child身上。被攔截的事件會轉到parent view的onTouchEvent方法中進行處理。但是這個交互過程還是挺複雜的,有多種情況,今天我們就來分析一下吧。這篇分析文章已經放了一段時間了,如果有任何問題請高人指出。
觸摸事件的分發
觸摸事件的攔截
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;
- }
- }
- 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>
情景分析
情景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
- // 事件攔截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
總結
- /**
- * {@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);
- }