真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

淺談onTouch先執(zhí)行,還是onClick執(zhí)行(詳解)

有一個(gè)Button 按鈕,要想為該按鈕設(shè)置onClick事件和OnTouch事件

目前成都創(chuàng)新互聯(lián)已為千余家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)絡(luò)空間、網(wǎng)站托管運(yùn)營(yíng)、企業(yè)網(wǎng)站設(shè)計(jì)、永寧網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。

mTestButton.setOnClickListener(new View.OnClickListener() {  
      @Override 
      public void onClick(View view) {  
        Log.d(TAG, "onClick execute");  
      }  
});  
mTestButton.setOnTouchListener(new View.OnTouchListener() {  
      @Override 
      public boolean onTouch(View view, MotionEvent motionEvent) {  
        Log.d(TAG, "onTouch execute, action event " + motionEvent.getAction());  
        return false;  
      }  
});  

此時(shí),我們現(xiàn)在分析一下,是onTouch先執(zhí)行,還是onClick執(zhí)行,接下來(lái)我從FrameWork 源碼去探尋一下整個(gè)事件的執(zhí)行流程和原理:

我們知道 Button ,TextView 等基礎(chǔ)控件的基類都是View,只要你觸摸到了任何一個(gè)控件,就一定會(huì)調(diào)用該控件的dispatchTouchEvent方法。那當(dāng)我們?nèi)c(diǎn)擊按鈕的時(shí)候,就會(huì)去調(diào)用Button類(實(shí)際上是基類View)里的dispatchTouchEvent方法,所以接下來(lái)看View源碼中dispatchTouchEvent()方法的具體實(shí)現(xiàn):

public boolean dispatchTouchEvent(MotionEvent event) {  
  if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
      mOnTouchListener.onTouch(this, event)) {  
    return true;  
  }  
  return onTouchEvent(event);  
}  

分析上述代碼,第2行 如果三個(gè)條件都為真的話,就返回true,否則執(zhí)行onTouchEvent,先看第一個(gè)條件mOnTouchListener!=null,這個(gè)條件就是如果設(shè)置了OnTouchListener就會(huì)為true,否則是false; 第二個(gè)條件(mViewFlags & ENABLED_MASK) == ENABLED是判斷當(dāng)前點(diǎn)擊的控件是否是enable的,按鈕默認(rèn)都是enable的,因此這個(gè)條件恒定為true;第三個(gè)條件就比較復(fù)雜了,mOnTouchListener.onTouch(this, event),這個(gè)其實(shí)就是去回調(diào)控件注冊(cè)touch事件時(shí)的onTouch方法。也就是說(shuō)如果我們?cè)趏nTouch方法里返回true,就會(huì)讓這三個(gè)條件全部成立,從而整個(gè)方法直接返回true。如果我們?cè)趏nTouch方法里返回false,就會(huì)再去執(zhí)行onTouchEvent(event)方法。onTouchEvent(MotionEvent event)方法同樣也是在view中定義的一個(gè)方法,主要是處理傳遞到view 的手勢(shì)事件,包括ACTION_DOWN,ACTION_MOVE,ACTION_UP,ACTION_CANCEL四種事件。

接下來(lái)我們結(jié)合上面的具體例子,來(lái)分析一下這個(gè)過程,首先會(huì)執(zhí)行dispatchTouchEvent(MotionEvent event) ,所以onTouch方法肯定是早于onClick方法的,如果在onTouch里返回false,就會(huì)出現(xiàn)下面的現(xiàn)象:

10-20 18:57:49.670: DEBUG/MainActivity(20153): onTouch execute, action event 0
10-20 18:57:49.715: DEBUG/MainActivity(20153): onTouch execute, action event 1
10-20 18:57:49.715: DEBUG/MainActivity(20153): onClick execute

即先執(zhí)行了onTouch,再執(zhí)行了onClick事件,而且onTouch執(zhí)行了兩次,一個(gè)是action_down,一個(gè)是action_up事件;

如果onTouch里返回true,則出現(xiàn)下面的現(xiàn)象:

10-20 19:01:59.795: DEBUG/MainActivity(21010): onTouch execute, action event 0
10-20 19:01:59.860: DEBUG/MainActivity(21010): onTouch execute, action event 1

結(jié)果是onClick事件沒有執(zhí)行了,原因是如果onTouch返回true的話,則dispatchEvent(MotionEvent event)方法直接返回true了,相當(dāng)于不往下傳遞事件了,所以onClick不會(huì)執(zhí)行,相反如果onTouch返回false的話(此時(shí)會(huì)執(zhí)行onClick方法),則會(huì)執(zhí)行 onTouchEvent(MotionEvent event)方法,由此可以得出這樣一個(gè)結(jié)論,onClick事件的具體調(diào)用執(zhí)行肯定是在onTouchEvent(MotionEvent event)方法源碼中,接下來(lái)分析一下該函數(shù)的源碼:

public boolean onTouchEvent(MotionEvent event) { 
  final int viewFlags = mViewFlags; 
  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 (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 & 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; 
          if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { 
            focusTaken = requestFocus(); 
          } 
          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(); 
            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; 
        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; 
        if ((x < 0 - slop) || (x >= getWidth() + slop) || 
            (y < 0 - slop) || (y >= getHeight() + slop)) { 
          // Outside button 
          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; 
}

雖然源碼有點(diǎn)多,但是我們只重點(diǎn)關(guān)注關(guān)鍵代碼,在38行我們看到了代碼:performClick();這個(gè)方法從名字表義來(lái)看就是OnClick方法的調(diào)用,我們進(jìn)入到該方法中去看一探究竟,是否執(zhí)行了OnClick方法呢?

public boolean performClick() {  
  sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);  
  if (mOnClickListener != null) {  
    playSoundEffect(SoundEffectConstants.CLICK);  
    mOnClickListener.onClick(this);  
    return true;  
  }  
  return false;  
} 

從上述代碼可以看到,只要mOnClickListener不是null,就會(huì)去調(diào)用它的onClick方法,那mOnClickListener又是在哪里賦值的呢?經(jīng)過分析后找到如下方法:

public void setOnClickListener(OnClickListener l) {  
  if (!isClickable()) {  
    setClickable(true);  
  }  
  mOnClickListener = l;  
} 

而上述這個(gè)方法就是我們?cè)贏pplication層經(jīng)常使用的方法,即我們給button 設(shè)置點(diǎn)擊事件的時(shí)候就會(huì)調(diào)用該方法了,分析到這了,我們知道了OnClick方法確實(shí)是在OnTouchEvent方法中,那么除了要設(shè)置 OnClickListener,調(diào)用onClick的條件又是什么呢?我們從38行代碼往前推,從第14行可以分析出:

只要該控件是可點(diǎn)擊的或者是長(zhǎng)按類型的,則會(huì)進(jìn)入到MotionEvent.ACTION_UP這個(gè)分支當(dāng)中 ,然后經(jīng)過各種條件判斷,則會(huì)進(jìn)入到38行的performClick()方法中。

至此,一切都清晰明白了!當(dāng)我們通過調(diào)用setOnClickListener方法來(lái)給控件注冊(cè)一個(gè)點(diǎn)擊事件時(shí),就會(huì)給mOnClickListener賦值。然后每當(dāng)控件被點(diǎn)擊時(shí)或者長(zhǎng)按時(shí),都會(huì)在performClick()方法里回調(diào)被點(diǎn)擊控件的onClick方法。

經(jīng)驗(yàn)之談:

關(guān)于OnTouchEvent(MotionEvent事件)事件的層級(jí)傳遞。我們都知道如果給一個(gè)控件注冊(cè)了touch事件,每次點(diǎn)擊它的時(shí)候都會(huì)觸發(fā)一系列的ACTION_DOWN,ACTION_MOVE,ACTION_UP等事件。這里需要注意,如果你在執(zhí)行ACTION_DOWN的時(shí)候返回了false,后面一系列其它的action就不會(huì)再得到執(zhí)行了。簡(jiǎn)單的說(shuō),就是當(dāng)dispatchTouchEvent在進(jìn)行事件分發(fā)的時(shí)候,只有前一個(gè)action返回true,才會(huì)觸發(fā)后一個(gè)action。

那我們可以換一個(gè)控件,將按鈕替換成ImageView,然后給它也注冊(cè)一個(gè)touch事件,并返回false。

以上這篇淺談onTouch先執(zhí)行,還是onClick執(zhí)行(詳解)就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持創(chuàng)新互聯(lián)。


網(wǎng)站名稱:淺談onTouch先執(zhí)行,還是onClick執(zhí)行(詳解)
本文鏈接:http://weahome.cn/article/jphhdh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部