最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入解析Andoird應(yīng)用開(kāi)發(fā)中View的事件傳遞

 更新時(shí)間:2016年02月18日 17:45:49   作者:iam_wingjay  
這篇文章主要介紹了深入解析Andoird應(yīng)用開(kāi)發(fā)中View的事件傳遞,其中重點(diǎn)講解了ViewGroup的事件傳遞流程,需要的朋友可以參考下

2016218174438007.jpg (602×339)

下面以點(diǎn)擊某個(gè)view之后的事件傳遞為例子。
首先分析view里的dispatchTouchEvent()方法,它是點(diǎn)擊view執(zhí)行的第一個(gè)方法。

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

注意:里面包含兩個(gè)回調(diào)函數(shù) onTouch(),onTouchEvent();如果控件綁定了OnTouchListener,且該控件是enabled,那么就執(zhí)行onTouch()方法,如果該方法返回true,則說(shuō)明該觸摸事件已經(jīng)被OnTouchListener監(jiān)聽(tīng)器消費(fèi)掉了,不會(huì)再往下分發(fā)了;但是如果返回false,則說(shuō)明未被消費(fèi),繼續(xù)往下分發(fā)到該控件的onTouchEvent()去處理。

然后分析onTouchEvent()方法,進(jìn)行進(jìn)一步的觸摸事件處理。

if (((viewFlags & CLICKABLE) == CLICKABLE || 
   (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { 
  switch (event.getAction()) { 
   case MotionEvent.ACTION_UP:
    ..... 
      performClick(); //響應(yīng)點(diǎn)擊事件 
    break; 
   case MotionEvent.ACTION_DOWN: 
   ..... break; 
   case MotionEvent.ACTION_CANCEL: 
   ..... break; 
   case MotionEvent.ACTION_MOVE: 
   ..... break; 
  } 
  return true; 
} 
return false;

如果該控件是clickable 、long_clickable的,那么就可以響應(yīng)對(duì)應(yīng)事件,響應(yīng)完后返回true繼續(xù)響應(yīng)。比如點(diǎn)擊事件,先響應(yīng)ACTION_DOWN,然后break并返回true,然后手抬起,又從dispatchTouchEvent()分發(fā)下來(lái),再響應(yīng)ACTION_UP,里面會(huì)去performClick()響應(yīng)點(diǎn)擊事件。

響應(yīng)點(diǎn)擊事件

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

里面執(zhí)行mOnClickListener.onClick(this);即回調(diào)綁定監(jiān)聽(tīng)器的onClick()函數(shù)。

關(guān)鍵點(diǎn):
onTouch和onTouchEvent的區(qū)別,又該如何使用?
答:
當(dāng)view控件接受到觸摸事件,如果控件綁定了onTouchListener監(jiān)聽(tīng)器,而且該控件是enable,那么就去執(zhí)行onTouch()方法,如果返回true,則已經(jīng)把觸摸事件消費(fèi)掉,不再向下傳遞;如果返回false,那么繼續(xù)調(diào)用onTouchEvent()事件。

Android的Touch事件傳遞到Activity頂層的DecorView(一個(gè)FrameLayout)之后,會(huì)通過(guò)ViewGroup一層層往視圖樹(shù)的上面?zhèn)鬟f,最終將事件傳遞給實(shí)際接收的View。下面給出一些重要的方法。

dispatchTouchEvent
事件傳遞到一個(gè)ViewGroup上面時(shí),會(huì)調(diào)用dispatchTouchEvent。代碼有刪減

public boolean dispatchTouchEvent(MotionEvent ev) {

  boolean handled = false;
  if (onFilterTouchEventForSecurity(ev)) {
    final int action = ev.getAction();
    final int actionMasked = action & MotionEvent.ACTION_MASK;

    // Attention 1 :在按下時(shí)候清除一些狀態(tài)
    if (actionMasked == MotionEvent.ACTION_DOWN) {
      cancelAndClearTouchTargets(ev);
      //注意這個(gè)方法
      resetTouchState();
    }

    // Attention 2:檢查是否需要攔截
    final boolean intercepted;
    //如果剛剛按下 或者 已經(jīng)有子View來(lái)處理
    if (actionMasked == MotionEvent.ACTION_DOWN
        || mFirstTouchTarget != null) {
      final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
      if (!disallowIntercept) {
        intercepted = onInterceptTouchEvent(ev);
        ev.setAction(action); // restore action in case it was changed
      } else {
        intercepted = false;
      }
    } else {
      // 不是一個(gè)動(dòng)作序列的開(kāi)始 同時(shí)也沒(méi)有子View來(lái)處理,直接攔截
      intercepted = true;
    }

     //事件沒(méi)有取消 同時(shí)沒(méi)有被當(dāng)前ViewGroup攔截,去找是否有子View接盤(pán)
    if (!canceled && !intercepted) {
        //如果這是一系列動(dòng)作的開(kāi)始 或者有一個(gè)新的Pointer按下 我們需要去找能夠處理這個(gè)Pointer的子View
      if (actionMasked == MotionEvent.ACTION_DOWN
          || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
          || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
        final int actionIndex = ev.getActionIndex(); // always 0 for down

        //上面說(shuō)的觸碰點(diǎn)32的限制就是這里導(dǎo)致
        final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
            : TouchTarget.ALL_POINTER_IDS;

        final int childrenCount = mChildrenCount;
        if (newTouchTarget == null && childrenCount != 0) {
          final float x = ev.getX(actionIndex);
          final float y = ev.getY(actionIndex);

          //對(duì)當(dāng)前ViewGroup的所有子View進(jìn)行排序,在上層的放在開(kāi)始
          final ArrayList<View> preorderedList = buildOrderedChildList();
          final boolean customOrder = preorderedList == null
              && isChildrenDrawingOrderEnabled();
          final View[] children = mChildren;
          for (int i = childrenCount - 1; i >= 0; i--) {
            final int childIndex = customOrder
                ? getChildDrawingOrder(childrenCount, i) : i;
            final View child = (preorderedList == null)
                ? children[childIndex] : preorderedList.get(childIndex);

               // canViewReceivePointerEvents visible的View都可以接受事件
               // isTransformedTouchPointInView 計(jì)算是否落在點(diǎn)擊區(qū)域上
            if (!canViewReceivePointerEvents(child)
                || !isTransformedTouchPointInView(x, y, child, null)) {
              ev.setTargetAccessibilityFocus(false);
              continue;
            }

               //能夠處理這個(gè)Pointer的View是否已經(jīng)處理之前的Pointer,那么把
            newTouchTarget = getTouchTarget(child);
            if (newTouchTarget != null) {
              // Child is already receiving touch within its bounds.
              // Give it the new pointer in addition to the ones it is handling.
              newTouchTarget.pointerIdBits |= idBitsToAssign;
              break;
            }              }
            //Attention 3 : 直接發(fā)給子View
            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
              // Child wants to receive touch within its bounds.
              mLastTouchDownTime = ev.getDownTime();
              if (preorderedList != null) {
                // childIndex points into presorted list, find original index
                for (int j = 0; j < childrenCount; j++) {
                  if (children[childIndex] == mChildren[j]) {
                    mLastTouchDownIndex = j;
                    break;
                  }
                }
              } else {
                mLastTouchDownIndex = childIndex;
              }
              mLastTouchDownX = ev.getX();
              mLastTouchDownY = ev.getY();
              newTouchTarget = addTouchTarget(child, idBitsToAssign);
              alreadyDispatchedToNewTouchTarget = true;
              break;
            }

          }
        }

      }
    }

    // 前面已經(jīng)找到了接收事件的子View,如果為NULL,表示沒(méi)有子View來(lái)接手,當(dāng)前ViewGroup需要來(lái)處理
    if (mFirstTouchTarget == null) {
      // ViewGroup處理
      handled = dispatchTransformedTouchEvent(ev, canceled, null,
          TouchTarget.ALL_POINTER_IDS);
    } else {

        if(alreadyDispatchedToNewTouchTarget) {
                   //ignore some code
          if (dispatchTransformedTouchEvent(ev, cancelChild,
              target.child, target.pointerIdBits)) {
            handled = true;
         }
        }

    }
  return handled;
}

上面代碼中的Attention在后面部分將會(huì)涉及,重點(diǎn)注意。

這里需要指出一點(diǎn)的是,一系列動(dòng)作中的不同Pointer可以分配給不同的View去響應(yīng)。ViewGroup會(huì)維護(hù)一個(gè)PointerId和處理View的列表TouchTarget,一個(gè)TouchTarget代表一個(gè)可以處理Pointer的子View,當(dāng)然一個(gè)View可以處理多個(gè)Pointer,比如兩根手指都在某一個(gè)子View區(qū)域。TouchTarget內(nèi)部使用一個(gè)int來(lái)存儲(chǔ)它能處理的PointerId,一個(gè)int32位,這也就是上層為啥最多只能允許同時(shí)最多32點(diǎn)觸碰。

看一下Attention 3 處的代碼,我們經(jīng)常說(shuō)view的dispatchTouchEvent如果返回false,那么它就不能系列動(dòng)作后面的動(dòng)作,這是為啥呢?因?yàn)锳ttention 3處如果返回false,那么它不會(huì)被記錄到TouchTarget中,ViewGroup認(rèn)為你沒(méi)有能力處理這個(gè)事件。

這里可以看到,ViewGroup真正處理事件是在dispatchTransformedTouchEvent里面,跟進(jìn)去看看:

dispatchTransformedTouchEvent
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
    View child, int desiredPointerIdBits) {

   //沒(méi)有子類(lèi)處理,那么交給viewgroup處理
  if (child == null) {
    handled = super.dispatchTouchEvent(transformedEvent);
  } else {
    final float offsetX = mScrollX - child.mLeft;
    final float offsetY = mScrollY - child.mTop;
    transformedEvent.offsetLocation(offsetX, offsetY);
    if (! child.hasIdentityMatrix()) {
      transformedEvent.transform(child.getInverseMatrix());
    }

    handled = child.dispatchTouchEvent(transformedEvent);
  }
  return handled;
}

可以看到這里不管怎么樣,都會(huì)調(diào)用View的dispatchTouchEvent,這是真正處理這一次點(diǎn)擊事件的地方。

dispatchTouchEvent
  public boolean dispatchTouchEvent(MotionEvent event) {
    if (onFilterTouchEventForSecurity(event)) {
    //先走View的onTouch事件,如果onTouch返回True
    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;
  }

我們給View設(shè)置的onTouch事件處在一個(gè)較高的優(yōu)先級(jí),如果onTouch執(zhí)行返回true,那么就不會(huì)去走view的onTouchEvent,而我們一些點(diǎn)擊事件都是在onTouchEvent中處理的,這也是為什么onTouch中返回true,view的點(diǎn)擊相關(guān)事件不會(huì)被處理。

小小總結(jié)一下這個(gè)流程
ViewGroup在接受到上級(jí)傳下來(lái)的事件時(shí),如果是一系列Touch事件的開(kāi)始(ACTION_DOWN),ViewGroup會(huì)先看看自己需不需要攔截這個(gè)事件(onInterceptTouchEvent,ViewGroup的默認(rèn)實(shí)現(xiàn)直接返回false表示不攔截),接著ViewGroup遍歷自己所有的View。找到當(dāng)前點(diǎn)擊的那個(gè)View,馬上調(diào)用目標(biāo)View的dispatchTouchEvent。如果目標(biāo)View的dispatchTouchEvent返回false,那么認(rèn)為目標(biāo)View只是在那個(gè)位置而已,它并不想接受這個(gè)事件,只想安安靜靜的做一個(gè)View(我靜靜地看著你們裝*)。此時(shí),ViewGroup還會(huì)去走一下自己dispatchTouchEvent,Done!

相關(guān)文章

  • Java常用集合與原理解析

    Java常用集合與原理解析

    這篇文章主要介紹了Java常用集合與原理解析,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03
  • Springboot?異步任務(wù)和定時(shí)任務(wù)的異步處理

    Springboot?異步任務(wù)和定時(shí)任務(wù)的異步處理

    本文介紹了Springboot異步任務(wù)和定時(shí)任務(wù)的異步處理,Springboot?中,異步任務(wù)和定時(shí)任務(wù)是經(jīng)常遇到的處理問(wèn)題方式,為了能夠用好這兩項(xiàng)配置,不干擾正常的業(yè)務(wù),需要對(duì)其進(jìn)行異步化配置。怎么設(shè)置合理的異步處理線程就是其核心和關(guān)鍵,下文詳情需要的朋友可以參考下
    2022-05-05
  • 利用Java連接Hadoop進(jìn)行編程

    利用Java連接Hadoop進(jìn)行編程

    這篇文章主要介紹了利用Java連接Hadoop進(jìn)行編程,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下
    2022-06-06
  • SpringBoot實(shí)現(xiàn)mysql與clickhouse多數(shù)據(jù)源的項(xiàng)目實(shí)踐

    SpringBoot實(shí)現(xiàn)mysql與clickhouse多數(shù)據(jù)源的項(xiàng)目實(shí)踐

    本文主要介紹了SpringBoot實(shí)現(xiàn)mysql與clickhouse多數(shù)據(jù)源的項(xiàng)目實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-11-11
  • Java中的CyclicBarrier循環(huán)柵欄解析

    Java中的CyclicBarrier循環(huán)柵欄解析

    這篇文章主要介紹了Java中的CyclicBarrier循環(huán)柵欄解析,從字面上的意思可以知道,這個(gè)類(lèi)的中文意思是"循環(huán)柵欄",大概的意思就是一個(gè)可循環(huán)利用的屏障,它的作用就是會(huì)讓所有線程都等待完成后才會(huì)繼續(xù)下一步行動(dòng),需要的朋友可以參考下
    2023-12-12
  • Springboot基于BCrypt非對(duì)稱加密字符串的實(shí)現(xiàn)

    Springboot基于BCrypt非對(duì)稱加密字符串的實(shí)現(xiàn)

    本文主要介紹了Springboot基于BCrypt非對(duì)稱加密字符串的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Java按行讀取文件文本內(nèi)容的方式匯總

    Java按行讀取文件文本內(nèi)容的方式匯總

    在工作和學(xué)習(xí)中,有時(shí)候會(huì)有一些場(chǎng)景,代碼需要配合讀取文件來(lái)執(zhí)行,比如:讀文件數(shù)據(jù),來(lái)進(jìn)行計(jì)算、組裝SQL、更新操作,本文給大家介紹在Java中按行讀取文件文件內(nèi)容的方式有哪些,感興趣的朋友一起看看吧
    2023-10-10
  • SWT(JFace)體驗(yàn)之RowLayout布局

    SWT(JFace)體驗(yàn)之RowLayout布局

    相對(duì)于FillLayout來(lái)說(shuō),RowLayout比較靈活,功能也比較強(qiáng)。用戶可以設(shè)置布局中子元素的大小、邊距、換行及間距等屬性。
    2009-06-06
  • Java使用協(xié)同過(guò)濾算法的代碼示例

    Java使用協(xié)同過(guò)濾算法的代碼示例

    在Java中實(shí)現(xiàn)協(xié)同過(guò)濾算法通常需要一些步驟,包括加載用戶-項(xiàng)目評(píng)分?jǐn)?shù)據(jù)、計(jì)算相似度、生成推薦等,以下是一個(gè)簡(jiǎn)化的基于用戶的協(xié)同過(guò)濾算法的代碼示例,感興趣的小伙伴跟著小編一起來(lái)看看吧
    2024-06-06
  • 通過(guò)spring注解開(kāi)發(fā),簡(jiǎn)單測(cè)試單例和多例區(qū)別

    通過(guò)spring注解開(kāi)發(fā),簡(jiǎn)單測(cè)試單例和多例區(qū)別

    這篇文章主要介紹了通過(guò)spring注解開(kāi)發(fā),簡(jiǎn)單測(cè)試單例和多例區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08

最新評(píng)論

达孜县| 周口市| 韶关市| 景泰县| 松桃| 金湖县| 托克逊县| 海晏县| 新邵县| 黔西县| 五寨县| 乌鲁木齐市| 若尔盖县| 确山县| 乡城县| 合肥市| 大余县| 金堂县| 德钦县| 启东市| 兴宁市| 高唐县| 蛟河市| 吐鲁番市| 西畴县| 炉霍县| 厦门市| 宽城| 纳雍县| 东兰县| 安泽县| 汕尾市| 华宁县| 南陵县| 南澳县| 环江| 甘南县| 开江县| 喀喇| 深州市| 怀来县|