日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國(guó)內(nèi)最全I(xiàn)T社區(qū)平臺(tái) 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁(yè) > php開(kāi)源 > 綜合技術(shù) > 自定義View系列教程06--詳解View的Touch事件處理

自定義View系列教程06--詳解View的Touch事件處理

來(lái)源:程序員人生   發(fā)布時(shí)間:2016-07-05 14:16:39 閱讀次數(shù):2533次

自定義View系列教程01--經(jīng)常使用工具介紹

自定義View系列教程02--onMeasure源碼詳實(shí)分析

自定義View系列教程03--onLayout源碼詳實(shí)分析

自定義View系列教程04--Draw源碼分析及其實(shí)踐

自定義View系列教程05--示例分析

自定義View系列教程06--詳解View的Touch事件處理

自定義View系列教程07--詳解ViewGroup分發(fā)Touch事件

自定義View系列教程08--滑動(dòng)沖突的產(chǎn)生及其處理


先上圖:


說(shuō)在前面:

View的事件分發(fā)簡(jiǎn)單記憶方法::dispathTouchEvent----->onTouchEvent------->onClick

如上圖,我把View的事件分發(fā)分為兩大塊:

第1塊:在dispatchTouchEvent()方法中。

       1 首先判斷當(dāng)前的OnTouchListener是不是為null。

       2 判斷當(dāng)前的控件是不是是ENABLED狀態(tài)。
       3 判斷onTouch方法返回的是true還是false。
         如果以上3步:
 有任何1個(gè)步驟返回false。那末就調(diào)用onTouchEvent(onTouchEvent返回true,則dispathTouchEvent 返回true;返回false,則dispathTouchEvent 返回false。)

        所有步驟都返回true。不調(diào)用其他方法,dispathTouchEvent ()返回true;

第2塊:在onTouchEvent方法中。

     1.當(dāng)控件不可用時(shí):

           當(dāng)控件有點(diǎn)擊事件,返回true,但不會(huì)調(diào)用onClick等點(diǎn)擊事件

           當(dāng)空間無(wú)點(diǎn)擊事件,返回false

      2.當(dāng)控件可用時(shí):

           無(wú)點(diǎn)擊事件,返回false

           有點(diǎn)擊事件,就走Switch()判斷,判斷是move,down,up,cancle,最后返回true

           在up時(shí),會(huì)調(diào)用preformClick()------>onClick()事件。

附錄:

//iamgeView調(diào)用view上面setOnTouchListener方法。 imageView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { //返回true,致使事件全部被響應(yīng) return false; } }); //給指定的iamgeView去設(shè)置1個(gè)點(diǎn)擊事件 imageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { ... } }); //點(diǎn)擊事件的處理規(guī)則,mOnClickListener甚么時(shí)候傳遞進(jìn)來(lái)的?? public boolean performClick() { ... if (mOnClickListener != null) { .... mOnClickListener.onClick(this); ... } ... }<pre name="code" class="java"> //view中setOnClickListener方法 public void setOnClickListener(OnClickListener l) { //如果當(dāng)前控件沒(méi)有點(diǎn)擊事件,設(shè)置1個(gè)點(diǎn)擊事件 if (!isClickable()) { setClickable(true); } mOnClickListener = l; }


--------------------------------我是華麗的分割線-----------------------ok請(qǐng)看正文---------------------------------------------------------------------------------------

在之前的幾篇文章中結(jié)合Andorid源碼還有示例分析完了自定義View的3個(gè)階段:measure,layout,draw。 在自定義View的進(jìn)程中我們還常常需要處理View的Touch事件,這就觸及到了大伙常說(shuō)的Touch事件的分發(fā)。其實(shí),這1部份還是有些復(fù)雜的,而且有的地方不是很好理解,特別是對(duì)剛上路的新司機(jī)來(lái)講常常理不清楚,欲求不滿,欲罷不能——想弄懂卻又覺(jué)得難,想放棄又覺(jué)得舍不得。

好吧,我也經(jīng)歷過(guò)這些痛楚,感同身受。

所以,我們就從相對(duì)而言比較簡(jiǎn)單的View的Touch事件處理入手開(kāi)始這部份知識(shí)的學(xué)習(xí)和總結(jié)。

滴滴,開(kāi)車了,車門(mén)行將關(guān)閉。上車請(qǐng)刷卡,沒(méi)卡的乘客請(qǐng)投幣。


如果1個(gè)View(比如Button)接收到Touch,那末該Touch事件首先會(huì)傳入到它的dispatchTouchEvent( )方法,所以我們從這里開(kāi)始學(xué)習(xí)View對(duì)Touch事件的處理。

/** * 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 (event.isTargetAccessibilityFocus()) { if (!isAccessibilityFocusedViewOrHost()) { return false; } event.setTargetAccessibilityFocus(false); } boolean result = false; if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } final int actionMasked = event.getActionMasked(); if (actionMasked == MotionEvent.ACTION_DOWN) { stopNestedScroll(); } if (onFilterTouchEventForSecurity(event)) { 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; } } if (!result && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } if (actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL || (actionMasked == MotionEvent.ACTION_DOWN && !result)) { stopNestedScroll(); } return result; }
嗯哼,這段源碼不長(zhǎng),除注釋就剩下不到100行了。該方法的輸入?yún)?shù)為event它表示Touch事件,這個(gè)很好理解;那末它的返回值有是甚么含義呢?該boolean值表示的是Touch事件是不是被消費(fèi)。

在此,對(duì)該部份源碼的核心部份和主要邏輯做1個(gè)梳理

第1步: 
調(diào)用TouchListener中的onTouch()處理Touch事件,請(qǐng)參見(jiàn)代碼第31⑶2行

該if判斷中1共包括了4個(gè)條件,必須同時(shí)滿足時(shí)才表示Touch事件被消費(fèi)

  1. li != null 
    ListenerInfo是View中的1個(gè)靜態(tài)類,包括了幾個(gè)Listener,比如TouchListener,F(xiàn)ocusChangeListener,LayoutChangeListeners,ScrollChangeListener等等。1般情況下它均不為null,所以我們不用過(guò)量關(guān)注它。
  2. li.mOnTouchListener != null 
    mOnTouchListener是由View設(shè)置的,比如mButton.setOnTouchListener()。所以如果View設(shè)置了Touch監(jiān)聽(tīng)那末,那末mOnTouchListener不空;反之,mOnTouchListener為null
  3. (mViewFlags & ENABLED_MASK) == ENABLED 
    當(dāng)前View可用(ENABLED)。通常可調(diào)用view.setEnabled( )設(shè)置View是不是可用
  4. li.mOnTouchListener.onTouch(this, event) 
    這1點(diǎn)實(shí)際上是在li.mOnTouchListener != null的基礎(chǔ)上繼續(xù)判斷。判斷TouchListener的onTouch( )方法是不是消耗了Touch事件。返回值為true表示消費(fèi)掉該事件,false表示未消費(fèi)。

在這4個(gè)條件中,我們通常最關(guān)心的就是最后1個(gè):TouchListener的onTouch()方法。假設(shè)這4個(gè)條件中的任意1個(gè)不滿足,那末result仍為false;則進(jìn)入下1步

第2步: 
調(diào)用View本身的onTouchEvent()處理Touch事件,請(qǐng)參見(jiàn)代碼第36⑶8行

if (!result && onTouchEvent(event)) { result = true; }
嗯哼,看到了吧:如果在上1步中Touch事件被消費(fèi)result為true,就不會(huì)履行這3行代碼了。該處調(diào)用了onTouchEvent()若該方法返回值false那末dispatchTouchEvent()的返回值也為false;反之,若該方法返回值為true,那末dispatchTouchEvent()的返回值亦為true。 

既然onTouchEvent()這么重要,我們就接著看該方法的源碼

public boolean onTouchEvent(MotionEvent event) { final float x = event.getX(); final float y = event.getY(); final int viewFlags = mViewFlags; final int action = event.getAction(); if ((viewFlags & ENABLED_MASK) == DISABLED) { if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); } return (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE); } if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; } } if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) { switch (action) { case MotionEvent.ACTION_UP: boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0; if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) { boolean focusTaken = false; if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { focusTaken = requestFocus(); } if (prepressed) { setPressed(true, x, y); } if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) { removeLongPressCallback(); if (!focusTaken) { 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)) { mUnsetPressedState.run(); } removeTapCallback(); } mIgnoreNextUpEvent = false; break; case MotionEvent.ACTION_DOWN: mHasPerformedLongPress = false; if (performButtonActionOnTouchDown(event)) { break; } boolean isInScrollingContainer = isInScrollingContainer(); if (isInScrollingContainer) { mPrivateFlags |= PFLAG_PREPRESSED; if (mPendingCheckForTap == null) { mPendingCheckForTap = new CheckForTap(); } mPendingCheckForTap.x = event.getX(); mPendingCheckForTap.y = event.getY(); postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); } else { setPressed(true, x, y); checkForLongClick(0); } break; case MotionEvent.ACTION_CANCEL: setPressed(false); removeTapCallback(); removeLongPressCallback(); mInContextButtonPress = false; mHasPerformedLongPress = false; mIgnoreNextUpEvent = false; break; case MotionEvent.ACTION_MOVE: drawableHotspotChanged(x, y); if (!pointInView(x, y, mTouchSlop)) { removeTapCallback(); if ((mPrivateFlags & PFLAG_PRESSED) != 0) { removeLongPressCallback(); setPressed(false); } } break; } return true; } return false; }
這段代碼略微復(fù)雜1些,在此分析幾個(gè)核心點(diǎn)。

  1. 當(dāng)View為disable時(shí)對(duì)Touch的處理,請(qǐng)參見(jiàn)代碼第7⑴6行。 
    若1個(gè)View是disable的,如果它是CLICKABLE或LONG_CLICKABLE或CONTEXT_CLICKABLE的就返回true,表示消耗掉了Touch事件。 
    但是請(qǐng)注意,該view所對(duì)應(yīng)的ClickListener.onClick( )不會(huì)有任何的響應(yīng)。即官方文檔的描寫(xiě):

    A disabled view that is clickable still consumes the touch events, it just doesn’t respond to them.

    若View雖然是disable的,但只要滿足這3個(gè)條件中的1個(gè),它就會(huì)消費(fèi)掉Touch事件但不再回調(diào)view的onClick( )方法

  2. 處理ACTION_DOWN,ACTION_MOVE,ACTION_UP事件等,請(qǐng)參見(jiàn)代碼第24⑴16行。 
    在此請(qǐng)注意在對(duì)ACTION_UP的處理時(shí)調(diào)用了performClick(),請(qǐng)參見(jiàn)代碼第50行。

    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; }
    在該方法中調(diào)用了view的mOnClickListener.onClick( ),請(qǐng)參見(jiàn)代碼第6行。 

    嗯哼,看到了吧:我們平常見(jiàn)得很多的Click事件是在View的onTouchEvent( )中處理ACTION_UP時(shí)調(diào)用的。

  3. 返回onTouchEvent()方法的輸出結(jié)果,請(qǐng)參見(jiàn)代碼第118⑴21行。 
    在該處請(qǐng)?zhí)貏e注意: 
    如果View是enable的,只要該View滿足CLICKABLE和LONG_CLICKABLE和CONTEXT_CLICKABLE這3者的任意1個(gè)(請(qǐng)參見(jiàn)代碼第24⑵6行)不論當(dāng)前的action是甚么,該onTouchEvent()返回的均是true(請(qǐng)參見(jiàn)代碼第118行);而且會(huì)在ACTION_UP時(shí)處理click事件。 
    同理,如果這3個(gè)條件都不滿足,該onTouchEvent()返回的是false。 
    也請(qǐng)注意1個(gè)細(xì)節(jié): 
    View的clickable屬性視不同的子View有所差異 
    比如:Button的clickable默許為true,但是TextView的clickable屬性默許為false。 
    View的longClickable屬性默許為false。 
    固然,我們可以通過(guò)代碼修改這些默許的屬性。 
    比如:setClickable()和setLongClickListener()可以改變View的CLICKABLE和LONG_CLICKABLE屬性。 
    除此之外,通過(guò)設(shè)置監(jiān)聽(tīng)器也可改變某些屬性。 
    比如:setOnClickListener()會(huì)將View的CLICKABLE設(shè)置為true;setOnLongClickListener()會(huì)將View的LONG_CLICKABLE設(shè)置為true。

第3步: 
返回Touch事件是不是被消費(fèi),請(qǐng)參見(jiàn)代碼第52行

以上就為View對(duì)Touch事件的主要步驟。 
在此我畫(huà)了1個(gè)簡(jiǎn)單的流程圖,現(xiàn)結(jié)合該圖和剛才的源碼分析對(duì)View的Touch事件處理流程做1個(gè)總結(jié)。

這里寫(xiě)圖片描述

  1. View處理Touch事件的整體流程 
    dispatchTouchEvent()—>onTouch()—>onTouchEvent()—>onClick() 
    Touch事件最早傳入dispatchTouchEvent()中;如果該View存在TouchListener那末會(huì)調(diào)用該監(jiān)聽(tīng)器中的onTouch()。在此以后如果Touch事件未被消費(fèi),則會(huì)履行到View的onTouchEvent()方法,在該方法中處理ACTION_UP事件時(shí)若該View存在ClickListener則會(huì)調(diào)用該監(jiān)聽(tīng)器中的onClick()
  2. onTouch()與onTouchEvent()和click3者的區(qū)分和聯(lián)系 
    2.1 onTouch()與onTouchEvent()都是處理觸摸事件的API 
    2.2 onTouch()屬于TouchListener接口中的方法,是View暴露給用戶的接口便于處理觸摸事件,而onTouchEvent()是Android系統(tǒng)本身對(duì)Touch處理的實(shí)現(xiàn) 
    2.3 先調(diào)用onTouch()后調(diào)用onTouchEvent()。而且只有當(dāng)onTouch()未消費(fèi)Touch事件才有可能調(diào)用到onTouchEvent()。即onTouch()的優(yōu)先級(jí)比onTouchEvent()的優(yōu)先級(jí)更高。 
    2.4 在onTouchEvent()中處理ACTION_UP時(shí)會(huì)利用ClickListener履行Click事件。所以Touch的處理是優(yōu)先于Click的 
    2.5 簡(jiǎn)單地說(shuō)3者履行順序?yàn)椋簅nTouch()–>onTouchEvent()–>onClick()
  3. View沒(méi)有事件的攔截(onInterceptTouchEvent( )),ViewGroup才有,請(qǐng)勿混淆 

關(guān)于View對(duì)Touch事件的處理就分析到此。

滴滴,到站了,下車的乘客們請(qǐng)往后門(mén)走。

原文鏈接:http://blog.csdn.net/lfdfhl/article/details/51559847



生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 91麻豆精品一二三区在线 | 91成人精品 | 精品成人在线 | 国产一区二区视频在线观看 | av在线不卡免费看 | 日日夜夜影院 | 精品一区三区 | 午夜精品一区二区三区在线 | 日韩一区av | 亚洲专区欧美专区 | 成人午夜小视频 | 亚洲福利一区二区 | 成人国产精品免费观看 | 国产一区二区三区在线 | 国产猛男猛女超爽免费视频 | 夫妻av | 亚洲精品国产成人 | 综合av第一页 | 久久久久久久国产精品影院 | 成人久久久精品国产乱码一区二区 | 欧美大吊视频 | 一区二区视频观看 | 国v精品久久久网 | 欧美日韩亚洲一区 | 日韩精品久久久久久 | www.久久.com| 欧美日精品 | 亚洲激情在线观看 | 国产午夜一区二区三区 | 性做久久久久久免费观看欧美 | 亚洲精品乱码久久久久v最新版 | 在线视频这里只有精品 | 免费成人av | 九九久久精品一区二区三区 | 日本中文字幕在线视频 | 国产视频久久 | 在线一区二区三区做爰视频网站 | 欧美精品一区二区久久 | 五月婷婷综合激情 | 久久久国产精品免费 | 18视频网站在线观看 |