Android中的Touch事件

 

Android中的Touch事件處理

 

主要內容

  Activity或View類的onTouchEvent()回調函數會接收到touch事件。html

  一個完整的手勢是從ACTION_DOWN開始,到ACTION_UP結束。android

  簡單的狀況下,咱們只須要在onTouchEvent()中寫個switch case語句,處理各類事件(Touch Down、 Touch Move、 Touch Up等),可是比較複雜的動做就須要更多的處理了。編程

  ViewGroup做爲一個parent是能夠截獲傳向它的child的touch事件的。app

  若是一個ViewGroup的onInterceptTouchEvent()方法返回true,說明Touch事件被截獲,子View再也不接收到Touch事件,而是轉向本ViewGroup的 onTouchEvent()方法處理。從Down開始,以後的Move,Up都會直接在onTouchEvent()方法中處理。ide

  先前還在處理touch event的child view將會接收到一個 ACTION_CANCEL函數

  若是onInterceptTouchEvent()返回false,則事件會交給child view處理。post

 

  Android中提供了ViewGroup、View、Activity三個層次的Touch事件處理。ui

  處理過程是按照Touch事件從上到下傳遞,再按照是否消費的返回值,從下到上返回,即若是View的onTouchEvent返回false,將會向上傳給它的parent的ViewGroup,若是ViewGroup不處理,將會一直向上返回到Activity。this

  即隧道式向下分發,而後冒泡式向上處理spa

 

Activity的Touch事件分發

  Activity的dispatchTouchEvent (MotionEvent ev):

 

    /**
     * Called to process touch screen events.  You can override this to
     * intercept all touch screen events before they are dispatched to the
     * window.  Be sure to call this implementation for touch screen events
     * that should be handled normally.
     *
     * @param ev The touch screen event.
     *
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

 

  處理屏幕觸摸事件,你能夠覆寫這個方法來截獲全部的觸摸屏幕事件,是在它們分發到窗口以前截獲。

  對於要正常處理的觸摸屏幕事件,要確認調用這個實現。

  返回值爲true的時候,代表這個事件被消費。

 

  Activity的onTouchEvent (MotionEvent event):

  

  

    /**
     * Called when a touch screen event was not handled by any of the views
     * under it.  This is most useful to process touch events that happen
     * outside of your window bounds, where there is no view to receive it.
     *
     * @param event The touch screen event being processed.
     *
     * @return Return true if you have consumed the event, false if you haven't.
     * The default implementation always returns false.
     */
    public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false;
    }

 

  若是一個屏幕觸摸事件沒有被這個Activity下的任何View所處理,Activity的onTouchEvent將會調用。

  這對於處理window邊界以外的Touch事件很是有用,由於一般是沒有View會接收到它們的。

  返回值爲true代表你已經消費了這個事件,false則表示沒有消費,默認實現中返回false。

 

View的Touch事件

  View的dispatchTouchEvent (MotionEvent event):

    /**
     * Pass the touch screen motion event down to the target view, or this
     * view if it is the target.
     *
     * @param event The motion event to be dispatched.
     * @return True if the event was handled by the view, false otherwise.
     */
    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 --> dispatchTouchEvent

 

  將touch屏幕的事件向下傳遞到目標View,或者傳遞到本View,若是它就是目標View。

  若是事件被這個View處理,則返回true,不然返回false。

  onTouchEvent (MotionEvent event):

 /**
     * Implement this method to handle touch screen motion events.
     *
     * @param event The motion event.
     * @return True if the event was handled, false otherwise.
     */
    public boolean onTouchEvent(MotionEvent event) {
        final int viewFlags = mViewFlags;

        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn't respond to them.
            return (((viewFlags & CLICKABLE) == CLICKABLE ||
                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
        }

        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }

        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_UP:
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        // take focus if we don't have it already and we should in
                        // touch mode.
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }

                        if (prepressed) {
                            // The button is being released before we actually
                            // showed it as pressed.  Make it show the pressed
                            // state now (before scheduling the click) to ensure
                            // the user sees it.
                            setPressed(true);
                       }

                        if (!mHasPerformedLongPress) {
                            // This is a tap, so remove the longpress check
                            removeLongPressCallback();

                            // Only perform take click actions if we were in the pressed state
                            if (!focusTaken) {
                                // Use a Runnable and post this rather than calling
                                // performClick directly. This lets other visual state
                                // of the view update before click actions start.
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClick();
                                }
                            }
                        }

                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }

                        if (prepressed) {
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }
                        removeTapCallback();
                    }
                    break;

                case MotionEvent.ACTION_DOWN:
                    mHasPerformedLongPress = false;

                    if (performButtonActionOnTouchDown(event)) {
                        break;
                    }

                    // Walk up the hierarchy to determine if we're inside a scrolling container.
                    boolean isInScrollingContainer = isInScrollingContainer();

                    // For views inside a scrolling container, delay the pressed feedback for
                    // a short period in case this is a scroll.
                    if (isInScrollingContainer) {
                        mPrivateFlags |= PFLAG_PREPRESSED;
                        if (mPendingCheckForTap == null) {
                            mPendingCheckForTap = new CheckForTap();
                        }
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    } else {
                        // Not inside a scrolling container, so show the feedback right away
                        setPressed(true);
                        checkForLongClick(0);
                    }
                    break;

                case MotionEvent.ACTION_CANCEL:
                    setPressed(false);
                    removeTapCallback();
                    removeLongPressCallback();
                    break;

                case MotionEvent.ACTION_MOVE:
                    final int x = (int) event.getX();
                    final int y = (int) event.getY();

                    // Be lenient about moving outside of buttons
                    if (!pointInView(x, y, mTouchSlop)) {
                        // Outside button
                        removeTapCallback();
                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                            // Remove any future long press/tap checks
                            removeLongPressCallback();

                            setPressed(false);
                        }
                    }
                    break;
            }
            return true;
        }

        return false;
    }
View --> onTouchEvent

 

  實現這個方法來處理touch屏幕的事件。

  返回true若是這個事件被處理了。

 

ViewGroup的Touch事件

  由於ViewGroup是View的子類,因此它覆寫方法時會加上Override註解,若是沒有覆寫,則沿用父類實現,如onTouchEvent()。

  dispatchTouchEvent (MotionEvent ev): 

/**
     * {@inheritDoc}
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
        }

        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            // Handle an initial down.
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                // Throw away all previous state when starting a new touch gesture.
                // The framework may have dropped the up or cancel event for the previous gesture
                // due to an app switch, ANR, or some other state change.
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

            // Check for interception.
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true;
            }

            // Check for cancelation.
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;

            // Update list of touch targets for pointer down, if needed.
            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
            if (!canceled && !intercepted) {
                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                    final int actionIndex = ev.getActionIndex(); // always 0 for down
                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                            : TouchTarget.ALL_POINTER_IDS;

                    // Clean up earlier touch targets for this pointer id in case they
                    // have become out of sync.
                    removePointersFromTouchTargets(idBitsToAssign);

                    final int childrenCount = mChildrenCount;
                    if (newTouchTarget == null && childrenCount != 0) {
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);
                        // Find a child that can receive the event.
                        // Scan children from front to back.
                        final View[] children = mChildren;

                        final boolean customOrder = isChildrenDrawingOrderEnabled();
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = customOrder ?
                                    getChildDrawingOrder(childrenCount, i) : i;
                            final View child = children[childIndex];
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                continue;
                            }

                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                // Child is already receiving touch within its bounds.
                                // Give it the new pointer in addition to the ones it is handling.
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }

                            resetCancelNextUpFlag(child);
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                                mLastTouchDownTime = ev.getDownTime();
                                mLastTouchDownIndex = childIndex;
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }
                        }
                    }

                    if (newTouchTarget == null && mFirstTouchTarget != null) {
                        // Did not find a child to receive the event.
                        // Assign the pointer to the least recently added target.
                        newTouchTarget = mFirstTouchTarget;
                        while (newTouchTarget.next != null) {
                            newTouchTarget = newTouchTarget.next;
                        }
                        newTouchTarget.pointerIdBits |= idBitsToAssign;
                    }
                }
            }

            // Dispatch to touch targets.
            if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                // Dispatch to touch targets, excluding the new touch target if we already
                // dispatched to it.  Cancel touch targets if necessary.
                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                while (target != null) {
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else {
                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                || intercepted;
                        if (dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)) {
                            handled = true;
                        }
                        if (cancelChild) {
                            if (predecessor == null) {
                                mFirstTouchTarget = next;
                            } else {
                                predecessor.next = next;
                            }
                            target.recycle();
                            target = next;
                            continue;
                        }
                    }
                    predecessor = target;
                    target = next;
                }
            }

            // Update list of touch targets for pointer up or cancel, if needed.
            if (canceled
                    || actionMasked == MotionEvent.ACTION_UP
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                resetTouchState();
            } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
                final int actionIndex = ev.getActionIndex();
                final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
                removePointersFromTouchTargets(idBitsToRemove);
            }
        }

        if (!handled && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
        }
        return handled;
    }
ViewGroup --> dispatchTouchEvent

 

  將Touch事件向下傳遞到目標View,由於自身也是View,因此目標View若是是本身,則傳遞給本身。

  返回true,若是這個事件是被本View所處理。

 

  onInterceptTouchEvent (MotionEvent ev)

  ViewGroup中比較特殊的一個方法。默認實現以下:

    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }

 

  這個方法註釋很長:

    /**
     * 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.
     *
     * <p>Using this function takes some care, as it has a fairly complicated
     * interaction with {@link View#onTouchEvent(MotionEvent)
     * 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:
     *
     * <ol>
     * <li> You will receive the down event here.
     * <li> 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.
     * <li> 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().
     * <li> 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 {@link MotionEvent#ACTION_CANCEL}, and all further
     * events will be delivered to your onTouchEvent() method and no longer
     * appear here.
     * </ol>
     *
     * @param ev The motion event being dispatched down the hierarchy.
     * @return Return true to steal motion events from the children and have
     * them dispatched to this ViewGroup through onTouchEvent().
     * The current target will receive an ACTION_CANCEL event, and no further
     * messages will be delivered here.
     */
ViewGroup onInterceptTouchEvent

 

  實現這個方法能夠截獲全部的Touch事件。這樣你就能夠控制向child分發的Touch事件。

  通常實現這個方法,須要同時實現View.onTouchEvent(MotionEvent)方法。

  事件是按照以下的順序被接收的:

  1.首先在onInterceptTouchEvent()中接收到Down事件。

  2.Down事件將會:要麼給這個ViewGroup的一個child view處理,要麼是這個ViewGroup本身的onTouchEvent()處理。

  處理意味着你應該在onTouchEvent()的實現中返回true,這樣你就能夠繼續看到這個gesture的其餘部分,若是返回false,將會返回尋找一個parent view去處理它。

  若是在onTouchEvent()中返回true,你將不會再在onInterceptTouchEvent()再收到接下來的事件,全部的Touch處理必須放在onTouchEvent()中正常處理。

  3.若是你在onInterceptTouchEvent()中返回false,接下來的每個事件都會先傳到onInterceptTouchEvent(),以後傳遞到目標view的onTouchEvent()中。

  4.若是你在onInterceptTouchEvent()中返回true,將不會再接收到手勢中的其餘事件,當前的目標view將會接收到同一個事件,可是動做是 ACTION_CANCEL。其餘全部的事件將會被直接傳遞到onTouchEvent()中,而且再也不在onInterceptTouchEvent()中出現。

  

  onInterceptTouchEvent()的返回值:true將會從子view中偷取運動事件,把它們分配到這個ViewGroup的onTouchEvent()中,當前目標view將會接收到取消動做,而且接下來的動做都不會再通過onInterceptTouchEvent()。

  ViewGroup的onTouchEvent()是採用父類View的默認實現,有須要的話能夠覆寫。

 

代碼示例

   寫了一個Demo觀察輸出和調用關係,代碼以下:

 

package com.mengdd.hellotouch;

import android.util.Log;
import android.view.MotionEvent;

public class Utils {

    public static void showMotionEventType(MotionEvent event, String logTag,
            String methodName) {

        final int action = event.getActionMasked();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                Log.i(logTag, methodName + ": " + action + ": ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i(logTag, methodName + ": " + action + ": ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i(logTag, methodName + ": " + action + ": ACTION_UP");
                break;
            case MotionEvent.ACTION_CANCEL:
                Log.i(logTag, methodName + ": " + action + ": ACTION_CANCEL");
                break;

            default:
                break;
        }

    }

    public static void showReturnValue(boolean returnValue, String logTag,
            String methodName) {
        Log.w(logTag, methodName + " return: " + returnValue);
    }

    public static void showInfo(String info, String logTag, String methodName) {
        Log.e(logTag, methodName + " info: " + info);
    }
}
Utils

 

package com.mengdd.hellotouch;

import com.example.helloscroller.R;

import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;

public class HelloTouchActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hello_scroller);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Utils.showMotionEventType(ev, "Activity", "dispatchTouchEvent");
        boolean result = super.dispatchTouchEvent(ev);

        Utils.showReturnValue(result, "Activity", "dispatchTouchEvent");
        return result;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        Utils.showMotionEventType(event, "Activity", "onTouchEvent");
        boolean result = super.onTouchEvent(event);
        Utils.showReturnValue(result, "Activity", "onTouchEvent");
        return result;
    }

}
Activity

 

package com.mengdd.hellotouch;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;

public class MyViewGroup extends LinearLayout {

    public MyViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyViewGroup(Context context) {
        super(context);
        init();
    }

    private void init() {

        setOnClickListener(mOnClickListener);
        setOnLongClickListener(mOnLongClickListener);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        Utils.showMotionEventType(event, "ViewGroup", "onInterceptTouchEvent");
        boolean returnValue = super.onInterceptTouchEvent(event);

        // This method JUST determines whether we want to intercept the motion.
        // If we return true, onTouchEvent will be called

        Utils.showReturnValue(returnValue, "ViewGroup", "onInterceptTouchEvent");
        return returnValue;

    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Utils.showMotionEventType(event, "ViewGroup", "dispatchTouchEvent");
        boolean returnValue = super.dispatchTouchEvent(event);
        Utils.showReturnValue(returnValue, "ViewGroup", "dispatchTouchEvent");
        return returnValue;
    }

    // ViewGroup本身的Touch事件處理,若是在onInterceptTouchEvent返回true,則會到這裏處理,不傳入child
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Utils.showMotionEventType(event, "ViewGroup", "onTouchEvent");

        boolean returnValue = super.onTouchEvent(event);
        Utils.showReturnValue(returnValue, "ViewGroup", "onTouchEvent");
        return returnValue;
    }

    private OnClickListener mOnClickListener = new OnClickListener() {

        @Override
        public void onClick(View v) {
            Utils.showInfo("onClick", "ViewGroup", "mOnClickListener");
            // onClick是ACTION_UP後調用的

        }
    };

    private OnLongClickListener mOnLongClickListener = new OnLongClickListener() {

        @Override
        public boolean onLongClick(View v) {
            // onLongClick按下到必定的時間就調用了
            Utils.showInfo("onLongClick", "ViewGroup", "mOnLongClickListener");
            // 若是返回false,則長按結束的ACTION_UP調用onClick
            // 若是返回true,onLongClick後再也不調用onClick
            return true;
        }
    };

}
ViewGroup
package com.mengdd.hellotouch;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

public class MyView extends TextView {

    public MyView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyView(Context context) {
        super(context);
    }

    private void init() {

        setOnClickListener(mOnClickListener);
        setOnLongClickListener(mOnLongClickListener);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Utils.showMotionEventType(event, "View", "dispatchTouchEvent");
        boolean returnValue = super.dispatchTouchEvent(event);
        Utils.showReturnValue(returnValue, "View", "dispatchTouchEvent");
        return returnValue;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Utils.showMotionEventType(event, "View", "onTouchEvent");
        boolean returnValue = super.onTouchEvent(event);
        Utils.showReturnValue(returnValue, "View", "onTouchEvent");

        return returnValue;
    }

    private OnClickListener mOnClickListener = new OnClickListener() {

        @Override
        public void onClick(View v) {
            Utils.showInfo("onClick", "View", "mOnClickListener");

        }
    };

    private OnLongClickListener mOnLongClickListener = new OnLongClickListener() {

        @Override
        public boolean onLongClick(View v) {
            Utils.showInfo("onLongClick", "View", "mOnLongClickListener");

            // 若是返回false,則長按結束的ACTION_UP調用onClick
            return false;
        }
    };

}
View
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".HelloTouchActivity" >

    <com.mengdd.hellotouch.MyViewGroup
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
        android:orientation="vertical">

        <com.mengdd.hellotouch.MyView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_gravity="center"
            android:text="Hello"
            android:background="#FFBBFFBB" />
    </com.mengdd.hellotouch.MyViewGroup>

</RelativeLayout>
layout


 

Click事件處理

  Click事件:View的短按和長按都是註冊監聽器的(setListener):

  onClick是在ACTION_UP以後執行的。

  onLongClick則是按下到必定時間以後執行的,這個時間是ViewConfiguration中的:

  private static final int TAP_TIMEOUT = 180; //180毫秒

  這裏須要注意onLongClick的返回值,若是是false,則onLongClick以後,手指擡起,ACTION_UP以後仍是回執行到onClick;可是若是onLongClick返回true,則不會再調用onClick。

 

參考資料

  本博客中:

  Android 觸摸手勢基礎 官方文檔概覽

  http://www.cnblogs.com/mengdd/p/3335508.html

  Android的Touch事件處理機制

  http://www.cnblogs.com/frydsh/archive/2012/11/08/2760408.html 

  Android FrameWork——Touch事件派發過程詳解

  http://blog.csdn.net/stonecao/article/details/6759189

  Android事件傳遞機制【Touch事件】

  http://orgcent.com/android-touch-event-mechanism/

  Android 編程下 Touch 事件的分發和消費機制

  http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html

相關文章
相關標籤/搜索