Android 點擊事件解析

Android 點擊事件解析,對Android Event事件做一個詳細整理。

測試驗證:
一:在Activity中隻有一個View控件,給View設置瞭onClickListener和onTouchListener事件

        test.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i("TAG","onClick");
            }
        });

        test.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        Log.i("TAG","onTouch  ACTION_DOWN");
                        break;
                    case MotionEvent.ACTION_MOVE:
                        Log.i("TAG","onTouch  ACTION_MOVE");
                        break;
                    case MotionEvent.ACTION_UP:
                        Log.i("TAG","onTouch  ACTION_UP");
                        break;
                }
                return false;
            }
        });

接下來對View 進行點擊測試,到底是View的點擊事件先執行還是onTouch先執行:

08-05 10:07:54.010 19588-19588/com.cms.testevent I/TAG: onTouch  ACTION_DOWN
08-05 10:07:54.070 19588-19588/com.cms.testevent I/TAG: onTouch  ACTION_UP
08-05 10:07:54.080 19588-19588/com.cms.testevent I/TAG: onClick

這是在點擊View之後馬上抬起,可以看到是先執行onTouch事件之後才到onClick事件。如果在按下View的時候手指稍微移動後在立馬抬起會執行Action_Move事件,如下:

08-05 10:12:03.810 19588-19588/com.cms.testevent I/TAG: onTouch  ACTION_DOWN
08-05 10:12:03.860 19588-19588/com.cms.testevent I/TAG: onTouch  ACTION_MOVE
08-05 10:12:03.870 19588-19588/com.cms.testevent I/TAG: onTouch  ACTION_UP
08-05 10:12:03.870 19588-19588/com.cms.testevent I/TAG: onClick

註意到onTouch有返回值,默認返回false,那這個返回值有什麼影響呢?我們進行測試:

08-05 10:19:06.290 5068-5068/com.cms.testevent I/TAG: onTouch  ACTION_DOWN
08-05 10:19:06.390 5068-5068/com.cms.testevent I/TAG: onTouch  ACTION_MOVE
08-05 10:19:06.400 5068-5068/com.cms.testevent I/TAG: onTouch  ACTION_MOVE
08-05 10:19:06.410 5068-5068/com.cms.testevent I/TAG: onTouch  ACTION_UP

測試發現onClick不在執行瞭,那這又是怎麼回事呢?我們從源碼中去尋找答案,找到View的dispatchTouchEvent方法。

    public boolean dispatchTouchEvent(MotionEvent event) {
        ....

        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

        ....

        return result;
    }

看關鍵代碼,在第10行到14行,判斷瞭li是否為空和li.onTouchListener是否為空,這個onTouchListener是什麼呢?找到onTouchListerner發現就是前面設置的setOnTouchListener()

    public void setOnTouchListener(OnTouchListener l) {
        getListenerInfo().mOnTouchListener = l;
    }

之後在判斷View是否是Enable的,默認是Enable的,然後調用瞭onTouchListener的onTouch方法,如果返回true,那麼4個條件全部滿足,result為true,如果result為true那麼接下來的onTouchEvent方法也就不在執行瞭,這也就驗證瞭為什麼onTouch返回true onClick事件就不在執行的原因。

接下來我們看下onTouchEvent的源碼,隻有在View是可點擊的情況下才會執行事件,前面設置瞭setOnClickListener所以會執行點擊事件,點擊事件是在performClick()裡面執行的。

public boolean onTouchEvent(MotionEvent event) {
       ....

        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
                (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    ...
                             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();
                                }
                            }
                    ...
                    break;

                case MotionEvent.ACTION_DOWN:
                    ...
                    break;

                case MotionEvent.ACTION_CANCEL:
                    ...
                    break;

                case MotionEvent.ACTION_MOVE:
            ...
                    break;
            }

            return true;
        }

        return false;
    }

點擊事件在performClick()裡被執行。

    public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }

關於點擊事件這一塊先說到這裡。

You May Also Like