Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中的Touch事件

Android中的Touch事件

編輯:關於Android編程

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

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

簡單的情況下,我們只需要在onTouchEvent()中寫個switch case語句,處理各種事件(Touch Down、Touch Move、Touch Up等),但是比較復雜的動作就需要更多的處理了。

ViewGroup作為一個parent是可以截獲傳向它的child的touch事件的。

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

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

如果onInterceptTouchEvent返回false,則事件會交給child view處理。

 

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

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

即隧道式向下分發,然後冒泡式向上處理。

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):

將touch屏幕的事件向下傳遞到目標view,或者傳遞到本view,如果他就是目標view。

如果事件被這個view處理,則返回true,否則返回false。

 

View的onTouchEvent

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;
    }
實現這個方法來處理touch屏幕的事件。

 

如果這個事件被處理了,就返回true。

 

ViewGroup的Touch事件

 

因為ViewGroup是View的子類,所以它覆寫方法時會加上Override注解,如果沒有覆寫,則沿用父類實現,如onTouchEvent()。

  dispatchTouchEvent (MotionEvent ev): 

\ViewGroup --> dispatchTouchEvent

 

將Touch事件向下傳遞到目標View,因為自身也是View,所以目標View如果是自己,則傳遞給自己。

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

 

onInterceptTouchEvent (MotionEvent ev)

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

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

 

這個方法注釋很長:

\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的默認實現,有需要的話可以覆寫。

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。


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved