安卓View.onTouchEvent(MotionEvent event) 方法一行一行讀



public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();//觸摸點(diǎn)x坐標(biāo)
        final float y = event.getY();//觸摸點(diǎn)y坐標(biāo)
        final int viewFlags = mViewFlags;//view的標(biāo)志,表示view的各種狀態(tài)
        final int action = event.getAction();//事件的類型,如down,move,up

/*
* 此段if作用:當(dāng)view為disabled時(shí),根據(jù)按下狀態(tài),點(diǎn)擊狀態(tài)等標(biāo)記決定是否消耗事件。
*/
//view的標(biāo)志經(jīng)過二進(jìn)制位與計(jì)算,結(jié)果若為Disabled(view被禁用了,無效了);源碼顯示常量DISABLED(禁用) == ENABLED_MASK(激活_屏蔽)==0x00000020。假如viewFlags也等于此常量,if(true)
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {//如果手指離開屏幕(action_up),并且標(biāo)識(shí)為按下((mPrivateFlags & PFLAG_PRESSED)結(jié)果不為0)
                setPressed(false);//設(shè)置按下的狀態(tài)為未按下(view禁用)
            }
            // A disabled view that is clickable still consumes the touch 
            // events, it just doesn't respond to them. 
            //這兩行注釋是連在一起的。譯文:一個(gè)無效視圖被點(diǎn)擊仍然會(huì)消耗觸摸事件,只是不會(huì)響應(yīng)它們。
            
            return (((viewFlags & CLICKABLE) == CLICKABLE  //是否為點(diǎn)擊
                    || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) // 或長按點(diǎn)擊
                    || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);//或上下文點(diǎn)擊(筆觸點(diǎn)擊,鼠標(biāo)右鍵等)
        }
        
        /*
        * 此段if作用:如果存在觸摸委托對(duì)象,用它來輔助管理事件。
        */
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {//將事件交付委托
                return true; //如果代理返回true執(zhí)行到此,消耗掉事件
            }
        }

/*
*   此段if作用:當(dāng)view不是禁用狀態(tài),并且事件也沒有交付TouchDelegate進(jìn)行處理,于是自己處理。
*/
// 此處和上方view禁用時(shí)return塊代碼一致,只是view禁用時(shí),無需處理
        if (((viewFlags & CLICKABLE) == CLICKABLE || //點(diǎn)擊
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || //長按點(diǎn)擊
                (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) { // 上下文點(diǎn)擊。
            
            switch (action) {//事件的動(dòng)作類型
                case MotionEvent.ACTION_UP: // 離開
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;// 位與計(jì)算。預(yù)警是否按下,否false,是true
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) { //按下或者預(yù)備按下,都會(huì)繼續(xù)
                        // take focus if we don't have it already and we should in
                        // touch mode. 譯文:如果沒有焦點(diǎn),我應(yīng)該在觸摸模式設(shè)置它
                        boolean focusTaken = false;//取得焦點(diǎn)的bool標(biāo)識(shí)
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { // 如果view可獲取焦點(diǎn),并且可以在觸摸模式下獲取焦點(diǎn),并且當(dāng)前沒有焦點(diǎn)。
                            focusTaken = requestFocus();//為view請(qǐng)求焦點(diǎn),返回并設(shè)置是否取得焦點(diǎn)的bool標(biāo)識(shí)
                        }

                        if (prepressed) { //view預(yù)備按下。
                            // 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. 譯文:設(shè)置此視圖的按下狀態(tài),并為動(dòng)畫提示提供觸摸坐標(biāo)。
                            setPressed(true, x, y);
                       }

                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {//如果長按點(diǎn)擊已被執(zhí)行,并且下一個(gè)事件不被忽略
                            // This is a tap, so remove the longpress check. 譯文:這是一個(gè)閥門,所以移除長按檢測(cè)
                            removeLongPressCallback();//移除長按檢測(cè)定時(shí)器

                            // Only perform take click actions if we were in the pressed state. 譯文:如果我們?cè)诎聪聽顟B(tài)下,只執(zhí)行點(diǎn)擊動(dòng)作。
                            if (!focusTaken) {//如果獲取焦點(diǎn)不成功
                                // Use a Runnable and post this rather than calling
                                // performClick directly. This lets other visual state
                                // of the view update before click actions start. 譯文:使用一個(gè)Runnable然后發(fā)送它,倒不如直接調(diào)用performClick()。這可以在單擊操作啟動(dòng)之前更新視圖的其他視覺狀態(tài)。
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {//如果發(fā)送mPerformClick到消息隊(duì)列中,如果不成功
                                    performClick();//直接調(diào)用PerformClick()方法,執(zhí)行click
                                }
                            }
                        }

                        if (mUnsetPressedState == null) {//如果未設(shè)置點(diǎn)擊狀態(tài)的Runnable為空
                            mUnsetPressedState = new UnsetPressedState();//new 一個(gè)。用來取消pressed的
                        }

                        if (prepressed) {//如果預(yù)備點(diǎn)擊
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());//發(fā)送runable,子view點(diǎn)擊狀態(tài)耗時(shí)
                        } else if (!post(mUnsetPressedState)) {//如果非預(yù)備點(diǎn)擊,且發(fā)送mUnsetPressedState到消息隊(duì)列不成功
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();//runable 跑起來
                        }

                        removeTapCallback();//移除閥門
                    }
                    mIgnoreNextUpEvent = false;//忽略下一個(gè)事件:false
                    break;

                case MotionEvent.ACTION_DOWN: //動(dòng)作:點(diǎn)下
                    mHasPerformedLongPress = false;//未執(zhí)行長按,才剛剛down,肯定未發(fā)生長按,所以初始使其值為false

                    if (performButtonActionOnTouchDown(event)) {//如果在觸摸事件中執(zhí)行按鈕動(dòng)作,break跳出。按鈕搶占焦點(diǎn)?使view點(diǎn)擊失效?
                        break;
                    }

                    // Walk up the hierarchy to determine if we're inside a scrolling container. 譯文:走上層次結(jié)構(gòu),以確定我們是否在滾動(dòng)容器。
                    boolean isInScrollingContainer = isInScrollingContainer();//是否在滾動(dòng)容器

                    // For views inside a scrolling container, delay the pressed feedback for
                    // a short period in case this is a scroll. 譯文:對(duì)于滾動(dòng)容器內(nèi)的視圖,延遲短時(shí)間的按下,以防滾動(dòng)。
                    if (isInScrollingContainer) {//如果在滾動(dòng)
                        mPrivateFlags |= PFLAG_PREPRESSED;//mPrivateFlags = mPrivateFlags | PFLAG_PREPRESSED;位或
                        if (mPendingCheckForTap == null) {//如果沒有待辦檢測(cè)閥門,也是一個(gè)runable
                            mPendingCheckForTap = new CheckForTap();//new 一個(gè)
                        }
                        mPendingCheckForTap.x = event.getX();//點(diǎn)下的x坐標(biāo)
                        mPendingCheckForTap.y = event.getY();//點(diǎn)下的y坐標(biāo)
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());//發(fā)送延遲時(shí)間,以檢測(cè)是長按還是普通點(diǎn)擊。
                    } else {//不在滾動(dòng)
                        // Not inside a scrolling container, so show the feedback right away. 譯文:不在滾動(dòng)的容器中,李即顯示反饋
                        setPressed(true, x, y);//設(shè)置點(diǎn)擊
                        checkForLongClick(0, x, y);//檢測(cè)長按點(diǎn)擊。此時(shí)不滾動(dòng),延遲為0
                    }
                    break;

                case MotionEvent.ACTION_CANCEL: //動(dòng)作:取消。所以將所有點(diǎn)擊動(dòng)作還原、清零
                    setPressed(false);// 設(shè)為為點(diǎn)擊
                    removeTapCallback();//移除閥門
                    removeLongPressCallback();//移除長按閥門
                    mInContextButtonPress = false;//上下文按鈕點(diǎn)擊false
                    mHasPerformedLongPress = false;//長按點(diǎn)擊未執(zhí)行
                    mIgnoreNextUpEvent = false;//不再忽略下一個(gè)事件
                    break;

                case MotionEvent.ACTION_MOVE: //動(dòng)作:滑動(dòng)(不放開,持續(xù))
                    drawableHotspotChanged(x, y);//將坐標(biāo)點(diǎn)傳下去。也許view本身或其子view熱點(diǎn)改變,需要傳播給Drawable,以作出相關(guān)改變

                    // Be lenient about moving outside of buttons. 譯文:對(duì)移動(dòng)從而超出范圍的按鈕寬讓
                    if (!pointInView(x, y, mTouchSlop)) {//如果坐標(biāo)不在view上
                        // Outside button
                        removeTapCallback();//移除閥門
                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {//不在view上,卻還是按下(?)
                            // Remove any future long press/tap checks. 譯文:移除任何未來的長按/閥門檢查
                            removeLongPressCallback();//移除長按

                            setPressed(false);//未點(diǎn)擊
                        }
                    }
                    break;
            }

            return true;//只要發(fā)生了點(diǎn)擊,長按點(diǎn)擊、,上下文點(diǎn)擊,就消耗掉事件
        }

        return false;//默認(rèn)不消耗事件
    }

讀完,有一種吐血三千丈,腦袋十個(gè)大的感覺。

另查到資料說:

正常點(diǎn)擊
0~500毫秒 Press
500+毫秒 LongPress
容器滾動(dòng)時(shí)
0~100毫秒 Prepress(預(yù)按下)
100~500毫秒 Press(按下)
500+毫秒 LongPress()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 之前就覺得docker 是個(gè)很吊的東西,也在電腦上裝了docker,但是一直都比較忙,所以也忘了,最近突然想起就重...
    samYau閱讀 665評(píng)論 0 3
  • 那年,在大街上偶遇你。 我春心萌動(dòng),暗自歡喜。 慢慢的接近你, 后來我告訴你,我喜歡你, 可我不知, 對(duì)你來說,我...
    記得我忘了你閱讀 176評(píng)論 0 0
  • 京東涉足醫(yī)藥早在2011年就有動(dòng)向。當(dāng)初他們注資九州通旗下的北京好藥師大藥房連鎖有限公司,占比49%,但僅合作了1...
    常有才閱讀 2,831評(píng)論 0 1

友情鏈接更多精彩內(nèi)容