<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                玉不琢,不成器;人不學,不知道。——《禮記·學記》 轉載請注明出處dmk877的博客[http://blog.csdn.net/dmk877/article/details/48781845](http://blog.csdn.net/dmk877/article/details/48781845) 在閱讀每一篇博客時一定要認真體會作者所想表達的意思,認真去揣摩,如果必要的話多讀幾遍,如果你讀懂這篇文章,那么你對View的事件分發機制肯定會有一個深刻的了解。如有謬誤歡迎大家批評指正我將在第一時間改正,如有疑問歡迎留言。知識只有拿過來分享討論我們才能共同進步。 在實際的開發中事件的分發可以說是經常用到的,比如自定義控件,或者處理界面中事件的沖突等等,當我們對android的事件的分發機制不了解時 遇到這種問題時你會感覺到無從下手,相反當你掌握了分發機制后你就會得心應手,因此熟練掌握android的事件分發機制是非常必要的,這也是面 中經常被問到的。廢話不多說進入正題 通過本篇博客你將學到: ①android中View的事件分發機制過程 ②View的點擊事件和長按事件的執行的控制 ③View的事件分發機制中常用的知識點 在詳細講解事件分發機制之前首先我們來看例子,這幾個例子可能大家會有疑惑,如果有的話你看完后就非常清楚是怎么回事了。 案例一 首先我們來一下第一個例子,這個例子非常簡單,就是對布局中的ImageView和Button 設置setOntouchListener監聽器代碼如下 ~~~ btnTest.setOnTouchListener( new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { System. out.println( "btnTest onTouch "+event.getAction()); return false; } }); ivLanucher.setOnTouchListener( new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { System. out.println( "ivLanucher onTouch "+event.getAction()); return false; } }); ~~~ 大家是否知道日志會怎么打印?可能有的人不清楚,那么我現在先把打印結果寫出來(注意:①點擊動作最好用模擬器測試,因為如果用真機的話,很容易觸發控件的Move時間②0表示down事件,1表示Up事件,2表示move事件) ①當我們點擊ImageView時的日志如下 ![](https://box.kancloud.cn/2016-02-23_56cbc62552d7c.jpg) ②當我們點擊Button時的日志如下 ![](https://box.kancloud.cn/2016-02-23_56cbc62564405.jpg) 案例二 把上述的setOnTouchListener的返回值都設置為True此時代碼如下 ~~~ btnTest.setOnTouchListener( new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { System. out.println( "btnTest onTouch "+event.getAction()); return true; } }); ivLanucher.setOnTouchListener( new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { System. out.println( "ivLanucher onTouch "+event.getAction()); return true; } }); ~~~ ①當我們點擊ImageView時的日志如下 ![](https://box.kancloud.cn/2016-02-23_56cbc62673a65.jpg) ②當我們點擊Button時的日志如下 ![](https://box.kancloud.cn/2016-02-23_56cbc62685db3.jpg) 案例三、自定義一個Button 自定義的Button代碼如下 ~~~ package com.example.dispatchtoucheventpractice; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.Button; public class MyButton extends Button { public MyButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public MyButton(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent. ACTION_DOWN: System. out.println( "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent. ACTION_MOVE: System. out.println( "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent. ACTION_UP: System. out.println( "dispatchTouchEvent ACTION_UP"); break; } return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent. ACTION_DOWN: System. out.println( "onTouchEvent ACTION_DOWN"); break; case MotionEvent. ACTION_MOVE: System. out.println( "onTouchEvent ACTION_MOVE"); break; case MotionEvent. ACTION_UP: System. out.println( "onTouchEvent ACTION_UP"); break; } return super.onTouchEvent(event); } } ~~~ MainActivity的代碼如下 ~~~ btnTest.setOnTouchListener( new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action=event.getAction(); switch (action) { case MotionEvent. ACTION_DOWN: System. out.println( "onTouch ACTION_DOWN"); break; case MotionEvent. ACTION_MOVE: System. out.println( "onTouch ACTION_MOVE"); break; case MotionEvent. ACTION_UP: System. out.println( "onTouch ACTION_UP"); break; } return false; } }); ~~~ 我們點擊按鈕并移動一小小段距離打印結果如下 ![](https://box.kancloud.cn/2016-02-23_56cbc6269753c.jpg) 你能解釋這個三個方法的執行順序為什么是這樣嗎? 在此基礎上我們把MainActivity中的自定義的Button的setOnTouchListener的返回值修改為true,點擊按鈕移動一段距離打印結果如下 ![](https://box.kancloud.cn/2016-02-23_56cbc626a6cca.jpg) 為什么onTouchEvent方法沒有執行? 好了這三個案例你是否都能從源碼的角度對其分析,如果不能,看完此篇博客,你會對上述的案例非常清楚,好了我們開始事件分發機制的源碼講解 首先我們必須明白對于一個view的事件的分析首先要從dispatchTouchEvent這個方法入手,先上源碼 ~~~ /** * 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 (mOnTouchListener != null && ( mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch( this, event)) { return true; } return onTouchEvent(event); } ~~~ 在這里如果dispatchTouchEvent的返回值為true則表示,這個事件被view所消費,反之則不消費,從源碼中看首先會進入一個if判斷語句,判斷的條件有三個 ①mOnTouchListener!=null; 第一個條件mOnTouchListener這個監聽器是什么?接著看源碼 ~~~ /** * Register a callback to be invoked when a touch event is sent to this view. * @param l the touch listener to attach to this view */ public void setOnTouchListener(OnTouchListener l) { mOnTouchListener = l; } ~~~ 明白了吧,這個監聽就是我們對View設置的監聽 ②(mViewFlags?&?*ENABLED_MASK*?) ==?*ENABLED* 第二個條件(?mViewFlags?&?*ENABLED_MASK*?) ==?*ENABLED* 條件是判斷View是否Enabled,一般的View都是enabled,除非你手動去設置,也就說第二個條件是滿足的。 *③*mOnTouchListener.onTouch(this,event) 第三個條件就是我們在activity設置setOnTouchListener后重寫的onTouch()方法的返回值,也就是說這個ouTouch的返回值是我們自己設定的,假如我們給View設置了OnTouchListener,并且使ouTouch方法的返回值為true,從dispatchTouchEvent的源碼中我們可以看出它就不會執行View的onTouchEvent(event)這個方法,并且此時dispatchTouchEvent的返回值為true,假如ouTouch方法的返回值為false此時View的dispatchTouchEvent的if語句的條件就為false那么就會執行View的onTouchEvent(event)這個方法,并且dispatchTouchEvent方法的返回值就是View的onTouchEvent(event)方法的返回值,到這里大家對onTouch和onTouchEvent這個兩個方法的執行順序清楚了吧。現在可以回過頭來看看案例三打印的日志的順序,可以自己分析出來了吧。 從dispatchTouchEvent方法中我們可以得出: 在進行事件分發時的執行順序是dispatchTouchEvent--->OnTouchListener的onTouch方法--->onTouchEvent方法 到這里你應該能從源碼的角度對案例三中的打印順序進行解釋,在腦子里回顧一下。。。。 接下來我們就來看onTouchEvent方法的源碼 ~~~ /** ? ? ?* 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; ? ? ? ? /** ? ? ? ? ?* 如果一個View是disabled, 并且該View是Clickable或者longClickable,? ? ? ? ? ?* onTouchEvent()就不執行下面的代碼邏輯直接返回true, 表示該View就一直消費Touch事件 ? ? ? ? ?*/ ? ? ? ? if ((viewFlags & ENABLED_MASK) == DISABLED) { ? ? ? ? ? ? // 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)); ? ? ? ? } ? ? ? ? /** ? ? ? ? ?* 如果此View有觸碰事件處理代理,那么將此事件交給代理處理 ? ? ? ? ?*/ ? ? ? ? if (mTouchDelegate != null) { ? ? ? ? ? ? if (mTouchDelegate.onTouchEvent(event)) { ? ? ? ? ? ? ? ? return true; ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? /** ? ? ? ? ?* 如果不可點擊(既不能單擊,也不能長按)則直接返回false ? ? ? ? ?*/ ? ? ? ? if (((viewFlags & CLICKABLE) == CLICKABLE || ? ? ? ? ? ? ? ? (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { ? ? ? ? ? ? switch (event.getAction()) { ? ? ? ? ? ? ? ? case MotionEvent.ACTION_UP: ? ? ? ? ? ? ? ? ? ? boolean prepressed = (mPrivateFlags & PREPRESSED) != 0; ? ? ? ? ? ? ? ? ? ? if ((mPrivateFlags & PRESSED) != 0 || prepressed) { ? ? ? ? ? ? ? ? ? ? ? ? // take focus if we don't have it already and we should in ? ? ? ? ? ? ? ? ? ? ? ? // touch mode. ? ? ? ? ? ? ? ? ? ? ? ? boolean focusTaken = false; ? ? ? ? ? ? ? ? ? ? ? ? /** ? ? ? ? ? ? ? ? ? ? ? ? ?* 是否需要獲得焦點及用變量focusTaken設置是否獲得了焦點. * 如果我們還沒有獲得焦點,但是我們在觸控屏下又可以獲得焦點,那么則請求獲得焦點 ? ? ? ? ? ? ? ? ? ? ? ? ?*/ ? ? ? ? ? ? ? ? ? ? ? ? if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? focusTaken = requestFocus(); ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? /** ? ? ? ? ? ? ? ? ? ? ? ? ?* 判斷是否進行了長按事件的返回值情況,如果為false則移除長按的延遲消息并繼續往下執行 ? ? ? ? ? ? ? ? ? ? ? ? ?*/ ? ? ? ? ? ? ? ? ? ? ? ? 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) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? mPrivateFlags |= PRESSED; ? ? ? ? ? ? ? ? ? ? ? ? ? ? refreshDrawableState(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? /** ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?* ViewConfiguration.getPressedStateDuration() 獲得的是按下效 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?* 果顯示的時間,由PRESSED_STATE_DURATION常量指定,在2.2中為125毫秒 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?*/ ? ? ? ? ? ? ? ? ? ? ? ? ? ? postDelayed(mUnsetPressedState, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ViewConfiguration.getPressedStateDuration()); ? ? ? ? ? ? ? ? ? ? ? ? } else if (!post(mUnsetPressedState)) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? // If the post failed, unpress right now ? ? ? ? ? ? ? ? ? ? ? ? ? ? mUnsetPressedState.run(); ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? removeTapCallback(); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? case MotionEvent.ACTION_DOWN: ? ? ? ? ? ? ? ? ? ? if (mPendingCheckForTap == null) { ? ? ? ? ? ? ? ? ? ? ? ? mPendingCheckForTap = new CheckForTap(); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? mPrivateFlags |= PREPRESSED; ? ? ? ? ? ? ? ? ? ? /** ? ? ? ? ? ? ? ? ? ? ?* 給mHasPerformedLongPress設置初始值為false ? ? ? ? ? ? ? ? ? ? ?*/ ? ? ? ? ? ? ? ? ? ? mHasPerformedLongPress = false; ? ? ? ? ? ? ? ? ? ? /** ? ? ? ? ? ? ? ? ? ? ?* 發送一個延遲消息延遲時間為ViewConfiguration.getTapTimeout()在2.2的源碼中此值為115毫秒 ? ? ? ? ? ? ? ? ? ? ?* 到達115毫秒后會執行CheckForTap()方法,如果在這115毫秒之間用戶觸摸移動了,則 ? ? ? ? ? ? ? ? ? ? ?* 刪除此消息.否則執行按下狀態,在CheckForTap()中檢查長按. ? ? ? ? ? ? ? ? ? ? ?*/ ? ? ? ? ? ? ? ? ? ? postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? case MotionEvent.ACTION_CANCEL: ? ? ? ? ? ? ? ? ? ? mPrivateFlags &= ~PRESSED; ? ? ? ? ? ? ? ? ? ? refreshDrawableState(); ? ? ? ? ? ? ? ? ? ? removeTapCallback(); ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? case MotionEvent.ACTION_MOVE: ? ? ? ? ? ? ? ? ? ? final int x = (int) event.getX(); ? ? ? ? ? ? ? ? ? ? final int y = (int) event.getY(); ? ? ? ? ? ? ? ? ? ? // Be lenient about moving outside of buttons ? ? ? ? ? ? ? ? ? ? int slop = mTouchSlop; ? ? ? ? ? ? ? ? ? ? /** ? ? ? ? ? ? ? ? ? ? ?* 當手指在View上面滑動超過View的邊界, ? ? ? ? ? ? ? ? ? ? ?*/ ? ? ? ? ? ? ? ? ? ? if ((x < 0 - slop) || (x >= getWidth() + slop) || ? ? ? ? ? ? ? ? ? ? ? ? ? ? (y < 0 - slop) || (y >= getHeight() + slop)) { ? ? ? ? ? ? ? ? ? ? ? ? // Outside button ? ? ? ? ? ? ? ? ? ? /** ? ? ? ? ? ? ? ? ? ? * 如果手指滑動超過Vie的邊界則移除DOWN事件中設置的檢測 ? ? ? ? ? ? ? ? ? ? */ ? ? ? ? ? ? ? ? ? ? ? ? removeTapCallback(); ? ? ? ? ? ? ? ? ? ? ? ? if ((mPrivateFlags & PRESSED) != 0) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? // Remove any future long press/tap checks ? ? ? ? ? ? ? ? ? ? ? ? ? ? removeLongPressCallback(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? // Need to switch from pressed to not pressed ? ? ? ? ? ? ? ? ? ? ? ? ? ? mPrivateFlags &= ~PRESSED; ? ? ? ? ? ? ? ? ? ? ? ? ? ? refreshDrawableState(); ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? } ? ? ? ? ? ? return true; ? ? ? ? } ? ? ? ? return false; ? ? } ~~~ 下面我們來拆分一下上面的源碼首先執行一個if判斷語句 ~~~ if ((viewFlags & ENABLED_MASK) == DISABLED) { // 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(((viewFlags & CLICKABLE) == CLICKABLE ||(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE))它的范圍這里把中間的代碼省略如下: ~~~ public boolean onTouchEvent(MotionEvent event) { 。。。。。。。。。。。 此處有省略 if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { 。。。。。。。。。。。 此處有省略 } return true; } return false; } ~~~ 從上面的簡化代碼中我們可以看出只要是進入了if判斷語句則onTouchEvent一定會返回true即消費事件,并且進入此if語句的條件為 此View是可以點擊的或者是可以長按的。 下面我們來拆分一下上面的源碼首先執行一個if判斷語句 ~~~ if ((viewFlags & ENABLED_MASK) == DISABLED) { // 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 )); } ~~~ 在上面的注釋中已經對其進行了說明,這里單獨拿出來再強調一下-----如果一個View是disabled, 并且該View是Clickable或者longClickable, onTouchEvent()就不執行下面的代碼邏輯直接返回true, 表示該View就一直消費Touch事件,這一點從上面的代碼可以看出,如果一個enabled的View,并且是clickable或者longClickable的,onTouchEvent()會執行下面的代碼邏輯并返回true,這一點從上面的省略代碼片段可以得出。 綜上,一個clickable或者longclickable的View是一直消費Touch事件的,而一般的View既不是clickable也不是longclickable的(即不會消費Touch事件,只會執行ACTION_DOWN而不會執行ACTION_MOVE和ACTION_UP)?Button是clickable的,可以消費Touch事件,但是我們可以通過setClickable()和setLongClickable()來設置View是否為clickable和longClickable。 接著我們來分析一下onTouchEvent中的事件上面的代碼中有比較詳細的注釋,我在這里再分析一下 ACTION_DOWN: ~~~ case MotionEvent.ACTION_DOWN: if (mPendingCheckForTap == null) { mPendingCheckForTap = new CheckForTap(); } mPrivateFlags |= PREPRESSED; mHasPerformedLongPress = false; postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); break; ~~~ 在這個方法中首先給mPrivateFlags設置一個PREPRESSED的標識,然后設置為mHasPerformedLongPress設置一個初始值false,接著會執行一個延遲 ~~~ postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); ~~~ 在這里ViewConfiguration.getTapTimeout()的值為115毫秒(注意以上源碼包括時間常量都是2.2源碼中,其他源碼可能會稍有不同)這個延遲有什么作用呢? 在給定的TapTimeout時間之內,用戶的觸摸沒有移動,就當作用戶是想點擊,而不是滑動.具體的做法是,將 CheckForTap的實例mPendingCheckForTap添加時消息隊例中,延遲執行。如果在這tagTimeout之間用戶觸摸移動了,則刪除此消息.否則執行按下狀態.然后檢查長按。 經過115毫秒的延遲后會執行CheckForTap方法,這個方法是干什么的呢?來看下源碼 ~~~ /** * ACTION_DOWN事件延遲115毫秒后調用 */ private final class CheckForTap implements Runnable { ? ? ? ? public void run() { ? ? ? ? ? ? mPrivateFlags &= ~PREPRESSED; ? ? ? ? ? ? mPrivateFlags |= PRESSED; ? ? ? ? ? ? refreshDrawableState(); ? ? ? ? ? ? /** ? ? ? ? ? ? ?* 如果View支持長按事件即View是LONG_CLICKABLE的則發送一個長按事件的檢測 ? ? ? ? ? ? ?*/ ? ? ? ? ? ? if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) { ? ? ? ? ? ? ? ? postCheckForLongClick(ViewConfiguration.getTapTimeout()); ? ? ? ? ? ? } ? ? ? ? } ? ? } ~~~ 這個方法可以看到如果View是LONG_CLICKABLE的就是執行postCheckForLongClick(ViewConfiguration.*getTapTimeout*())這個方法來檢測長按事件,但是一般的View不是LONG_CLICKABLE的,可能有的人會有疑問,如果View不是LONG_CLICKABLE的怎么執行長按事件啊?此時我們需要調用setOnLongClickListener實現OnLongClickListener接口 源碼如下: ~~~ /** * Register a callback to be invoked when this view is clicked and held. If this view is not * long clickable, it becomes long clickable. * * @param l The callback that will run * * @see #setLongClickable(boolean) */ public void setOnLongClickListener (OnLongClickListener l) { if (!isLongClickable()) { setLongClickable( true); } mOnLongClickListener = l; } ~~~ 從源碼中我們可以看到設置了OnLongClickListener后如果這個View不是LONG_CLICKABLE的,那么就把它設置成LONG_CLICKABLE的。這樣我們回到CheckForTap方法在View是LONG_CLICKABLE的情況下就會調用postCheckForLongClick方法,這個方法的源碼如下 ~~~ private void postCheckForLongClick(int delayOffset) { /** * 設置mHasPerformedLongPress為false表示長按事件還未觸發 */ ? ? ? ? mHasPerformedLongPress = false; ? ? ? ? if (mPendingCheckForLongPress == null) { ? ? ? ? ? ? mPendingCheckForLongPress = new CheckForLongPress(); ? ? ? ? } ? ? ? ? mPendingCheckForLongPress.rememberWindowAttachCount(); ? ? ? ? /** ? ? ? ? ?* 此delayOffset是從上面的CheckForTap類中傳過來的值為ViewConfiguration.getTapTimeout() ? ? ? ? ?* ViewConfiguration.getLongPressTimeout()在2.2中為500毫秒,也就是經過500-115毫秒后會執行CheckForLongPress方·· * 法在CheckForLongPress方法中會調用執行長按事件的方法,由于在ACTION_DOWN事件中有一個延遲消息延遲115毫秒后 * 執行CheckForTap中的run方法所以這里500-115+115=500也就是說從按下起經過500毫秒會觸發長按事件的執行 ? ? ? ? ?*/ ? ? ? ? postDelayed(mPendingCheckForLongPress,ViewConfiguration.getLongPressTimeout() - delayOffset); ? ? } ~~~ 在上面的方法會有一個延遲經過500-115毫秒后會執行CheckForLongPress方法。 ~~~ class CheckForLongPress implements Runnable { ? ? ? ? private int mOriginalWindowAttachCount; ? ? ? ? /** ? ? ? ? ?* 因為等待形成長按的過程中,界面可能發生變化如Activity的pause及restart,這個時候,長按應當失效. ? ? ? ? ?* View中提供了mWindowAttachCount來記錄View的attach次數.當檢查長按時的attach次數與長按到形成時. ? ? ? ? ?* 的attach一樣則處理,否則就不應該再當前長按. 所以在將檢查長按的消息添加時隊伍的時候,要記錄下當前的windowAttach *Count. ? ? ? ? ?*/ ? ? ? ? public void run() { ? ? ? ? ? ? if (isPressed() && (mParent != null) ? ? ? ? ? ? ? ? ? ? && mOriginalWindowAttachCount == mWindowAttachCount) { ? ? ? ? ? ? ? ? if (performLongClick()) { ? ? ? ? ? ? ? ? /** ? ? ? ? ? ? ? ? * 執行長按事件后返回值為true,設置mHasPerformedLongPress為true此時會屏蔽點擊事件 ? ? ? ? ? ? ? ? */ ? ? ? ? ? ? ? ? ? ? mHasPerformedLongPress = true; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? public void rememberWindowAttachCount() { ? ? ? ? ? ? mOriginalWindowAttachCount = mWindowAttachCount; ? ? ? ? } ? ? } ~~~ 從CheckForLongPress的run方法中可以看到如果performLongClick()的返回值為true mHasPerformedLongPress才為true,那么我們看看performLongClick它的做了哪些動作呢?我們來看看源碼 ~~~ /** * Call this view's OnLongClickListener, if it is defined. Invokes the context menu * if the OnLongClickListener did not consume the event. * * @return True there was an assigned OnLongClickListener that was called, false * otherwise is returned. */ public boolean performLongClick() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); boolean handled = false; /** * 到了重點可以看到在這里會執行我們為View設置的長按事件的回調,這里的mOnLongClickListener就是我們自己給View設置的長按的監聽, ~~~ ~~~ * 從這里也可以得出一個結論即長按事件是在ACTION_DOWN中執行的 */ if (mOnLongClickListener != null) { handled = mOnLongClickListener.onLongClick(View.this); } if (!handled) { handled = showContextMenu(); } if (handled) { performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); } return handled; } ~~~ 終于來了個重點我們看到在其中有個判斷 ~~~ if (mOnLongClickListener != null) { handled = mOnLongClickListener.onLongClick(View.this); } ~~~ 也就是說如果你設置了長按的監聽,那么mOnLongClickListener!=null此時就會執行我們重寫的onLongClick()方法,這里我們也得出一個結論: 即長按事件是在ACTION_DOWN中執行的。 到這里我們可以總結一下:首先當執行ACTION_DOWN事件后會設置一個PREPRESSED標識,如果這次點擊持續115毫秒后就會發送一個檢測長按的延遲任務,這個任務的延遲時間是500-115毫秒,這個115毫秒就是檢測PREPRESSED所經歷的時間,所以這樣算一下就可以知道當按鈕從按下的那一刻起經歷了500毫秒就會觸發長按事件(注意這個Android 2.2中的源碼,其它的系統的時間會稍有差異) 通過以上的分析我們還可以得出如下結論: 1、如果此時設置了長按的回調,則執行長按時的回調,且如果長按的回調返回true;才把mHasPerformedLongPress置為ture; 2、否則,如果沒有設置長按回調或者長按回調返回的是false;則mHasPerformedLongPress依然是false; 一般的View默認是不消費touch事件的,我們要想執行點擊事件必須要調用setOnClickListener()來設置OnClickListener接口,我們看看這個方法的源碼就知道了 ~~~ public void setOnClickListener (OnClickListener l) { if (!isClickable()) { setClickable( true); } mOnClickListener = l; } ~~~ 看到沒?當我們設置了onClickListener時如果isClickable()=false,就執行?setClickable(true)。 到這里我們就可以分析案例一和二: 在案例一中當Button和ImageView都返回false時dispatchTouchEvent?的if語句不成立就會執行onTouchEvent方法,而在onTouchEvent中有?if?(((viewFlags &?CLICKABLE) ==?CLICKABLE?||(viewFlags &?LONG_CLICKABLE) ==?LONG_CLICKABLE?))這個if判斷語句因為ImageView的CLICKABLE=fasle,Button的CLICKABLE=true。所以Button可以進入這個if語句而ImageView不行,從源碼中可以看到(結合我們簡化的代碼看)只要進入這個if語句那么onTouchEvent一定返回true從而dispatchTouchEvent?返回true而消耗事件,如果沒有進入這個if語句那么onTouchEvent一定返回false從而dispatchTouchEvent一定返回fasle不消耗事件。而當ImageView和Button都返回true時此時dispatchTouchEvent的第一個語句成立并且返回值為true表示消費所有事件。所以此時它倆的打印內容是一樣的。 ACTION_MOVE: 當執行move事件時首先會拿到當前觸摸的X,Y坐標然后判斷當前觸摸的點有沒有移出當前的View,如果移出了當前的View可分為兩種情況: ①ACTION_DOWN事件觸發不到115毫秒時:首先執行removeTapCallback方法這個方法是移除在ACTION_DOWN方法中設置的PREPRESSED檢測 ? 這也和我們在ACTION_DOWN中的CheckForTap延遲115毫秒后執行進行的說明相吻合。 ②ACTION_DOWN事件觸發時間大于115毫秒時:此時已經觸發了長按的事件,當前mPrivateFlags一定為PRESSED且發送了長按的檢測,此時就會移除removeLongPressCallback()然后把mPrivateFlags中PRESSED標識去除 ACTION_UP: ACTION_UP在上面的注釋已經很清楚在這里我想對我們需要重重重點了解的內容分析一下。 在ACTION_UP執行的過程中有這么一個判斷 ~~~ 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語句里進行了什么操作如果mPerformClick!=null的話就會執行performClick()方法我們看看此方法干了什么 ~~~ /** * Call this view's OnClickListener, if it is defined. * * @return True there was an assigned OnClickListener that was called, false * otherwise is returned. */ public boolean performClick() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); if (mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); mOnClickListener.onClick(this); return true; } return false; } ~~~ 終于看到了,終于看到我們設置的監聽執行了,可以看到在這里如果mOnclickListener!=null就會執行onClick(),這里的mOnclickListener就是我們設置的監聽從而我們可以得出一條結論:onClick()方法是在ACTION_UP中執行的。 接著反過來我們回過頭看看上面那個if(!mHasPerformedLongPress)在這個判斷語句這個判斷語句執行的條件就是mHasPerformedLongPress為false 看到mHasPerformedLongPress你是不是感覺很熟悉,這個mHasPerformedLongPress不就是剛開始在ACTION_DOWN中我們給它設置個初始值false然后如果CheckForLongPress中的performLongClick()方法返回true則mHasPerformedLongPress為true,如果performLongClick()方法的返回值為false那么mHasPerformedLongPress為false與此同時如果mHasPerformedLongPress為false的話就不會進入if語句也就是說我們的onClick()方法就不會執行,我這樣說你能明白mHasPerformedLongPress這個字段的作用嗎?這個字段可以用來控制onLongClick()和onClick()的執行,這樣說肯定會有好多人不明白怎么回事,不知道大家有沒有一個疑問:對一個View它的OnLongClickListener和OnClickListener是否只能執行一個?怎樣控制這兩個方法的執行? 接下來通過一個例子你就會非常明白以上兩個問題,下面這個例子需要結合我們再ACTION_DOWN中的分析中最后得出的兩條結論。 ~~~ btnClick.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { Toast. makeText(MainActivity.this,"點擊事件",0) .show(); } }); btnClick.setOnLongClickListener( new OnLongClickListener() { @Override public boolean onLongClick(View v) { Toast. makeText(MainActivity.this,"長按事件",0).sh ow(); return false; } }); ~~~ 運行后我們長按按鈕發現會觸發長按事件彈出“長按事件”,松開按鈕后會觸發點擊事件彈出“點擊事件”, 分析: 因為我們的setOnsetOnLongClickListener中的onLongClick返回值為false導致performLongClick方法的返回值為false此時雖然執行了長按的方法但是不會進入下面if語句也就是mHasPerformedLongPress仍然為false ~~~ if (performLongClick()) { mHasPerformedLongPress = true; } ~~~ 當mHasPerformedLongPress為false的時候就是在ACTION_UP中執行performClick()方法從而執行點擊事件 當setOnLongClickListener的返回值為true時,當我們快速點擊按鈕時會觸發點擊事件彈出“點擊事件”,但是當我們觸發了長按事件后就不會觸發點擊事件,此時長按事件屏蔽了點擊事件。 分析: 當setOnLongClickListener返回值為true時,performLongClick方法的返回值也為true此時會從新給mHasPerformedLongPress賦值為true,當mHasPerformedLongPress為true的時候不會進入ACTION_UP中執行performClick()方法從而不會執行點擊事件。 不知道通過上面的分析大家有沒有理解,如果沒有理解的話就多讀幾遍吧。 總結: 1.整個View的事件分發的流程是 View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent 在dispatchTouchEvent中會進行OnTouchListener的判斷,如果OnTouchListener不為null且返回true,則表示事件被消費,onTouchEvent不會被執行;否則執行onTouchEvent。 2.一個clickable或者longClickable的View會永遠消費Touch事件,不管他是enabled還是disabled的 3.View的長按事件是在ACTION_DOWN中執行,要想執行長按事件該View必須是longClickable的,并且不能產生ACTION_MOVE 4.View的點擊事件是在ACTION_UP中執行,想要執行點擊事件的前提是消費了ACTION_DOWN和ACTION_MOVE,并且沒有設置OnLongClickListener的情況下,如設置了OnLongClickListener的情況,則必須使onLongClick()返回false 5.如果View設置了onTouchListener了,并且onTouch()方法返回true,則不執行View的onTouchEvent()方法,也表示View消費了Touch事件,返回false則繼續執行onTouchEvent() [Android事件分發機制--ViewGroup(二)](http://blog.csdn.net/dmk877/article/details/49055815) 好了這一篇就到這里了,如果你覺得還不錯,就留個言頂一下吧。哈哈。。。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看