欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android Touch事件分發(fā)深入了解

 更新時(shí)間:2016年04月04日 19:01:20   作者:absfree  
這篇文章主要為大家詳細(xì)介紹了Android Touch事件分發(fā),內(nèi)容很詳細(xì),感興趣的朋友可以參考一下

本文帶著大家深入學(xué)習(xí)觸摸事件的分發(fā),具體內(nèi)容如下
1. 觸摸動(dòng)作及事件序列

(1)觸摸事件的動(dòng)作

    觸摸動(dòng)作一共有三種:ACTION_DOWN、ACTION_MOVE、ACTION_UP。當(dāng)用戶手指接觸屏幕時(shí),便產(chǎn)生一個(gè)動(dòng)作為ACTION_DOWN的觸摸事件,此時(shí)若用戶的手指立即離開屏幕,會(huì)產(chǎn)生一個(gè)動(dòng)作為ACTION_UP的觸摸事件;若用戶手指接觸屏幕后繼續(xù)滑動(dòng),當(dāng)滑動(dòng)距離超過了系統(tǒng)中預(yù)定義的距離常數(shù),則產(chǎn)生一個(gè)動(dòng)作為ACTION_MOVE的觸摸事件,系統(tǒng)中預(yù)定義的用來判斷用戶手指在屏幕上的滑動(dòng)是否是一個(gè)ACTION_MOVE動(dòng)作的這個(gè)距離常量叫做TouchSlop,可通過ViewConfiguration.get(getContext()).getScaledTouchSlop()獲取。

(2)事件序列

    當(dāng)用戶的手指接觸屏幕,在屏幕上滑動(dòng),又離開屏幕,這個(gè)過程會(huì)產(chǎn)生一系列觸摸事件:ACTION_DOWN-->若干個(gè)ACTION_MOVE-->ACTION_UP。這一系列觸摸事件即為一個(gè)事件序列。 

2. 觸摸事件的分發(fā)

(1)概述

    當(dāng)產(chǎn)生了一個(gè)觸摸時(shí)間后,系統(tǒng)要負(fù)責(zé)把這個(gè)觸摸事件給一個(gè)View(TargetView)來處理,touch事件傳遞到TargetView的過程即為touch事件的分發(fā)。

    觸摸事件的分發(fā)順序:Activity-->頂級View-->頂級View的子View-->. . .-->Target View

    觸摸事件的響應(yīng)順序:TargetView --> TargetView的父容器 --> . . . -->頂級View -->Activity

(2)toush事件分發(fā)的具體過程

  a. Activity對touch事件的分發(fā)

    當(dāng)用戶手指接觸屏幕時(shí),便產(chǎn)生了一個(gè)touch事件,封裝了touch事件的MotionEvent最先被傳遞給當(dāng)前Activity,Activity的dispatchTouchEvent方法負(fù)責(zé)touch事件的分發(fā)。分發(fā)touch事件的實(shí)際工作由當(dāng)前Activity的Window完成,而Window會(huì)將touch事件傳遞給DecorView(當(dāng)前用戶界面頂級View)。Activity的dispatchTouchEvent方法代碼如下:

public boolean dispatchTouchEvent(MotionEvent ev) {
  if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    onUserInteraction();
  }
  if (getWindow().superDispatchTouchEvent(ev)) {
    return true;
  }
  return onTouchEvent(ev);
}

    根據(jù)以上代碼可以知道,touch事件會(huì)交由Window的superDispatchTouchEvent進(jìn)行分發(fā),若這個(gè)方法返回true,意味touch事件的分發(fā)過程結(jié)束,返回false則說明經(jīng)過層層分發(fā),沒有子View對這個(gè)事件進(jìn)行處理,即所有子View的onTouchEvent方法都返回false(即這個(gè)touch事件沒有被“消耗”)。這時(shí)會(huì)調(diào)用Activity的onTouchEvent方法來處理這個(gè)touch事件。

    在Window的superDispatchTouchEvent方法中,首先會(huì)把touch事件分發(fā)給DecorView,因?yàn)樗钱?dāng)前用戶界面的頂級View。Window的superDispatchTouchEvent方法如下:

public abstract boolean superDispatchTouchEvent(MotionEvent ev);
    是個(gè)抽象方法,這個(gè)方法由Window的實(shí)現(xiàn)類PhoneWindow實(shí)現(xiàn),PhoneWindow的superDispatchTouchEvent方法的代碼如下:

public boolean superDispatchTouchEvent(MotionEvent ev) {
  return mDecor.superDispatchTouchEvent(event);
}

    由以上代碼可得,PhoneWindow的superDispatchTouchEvent方法實(shí)際上是通過DecorView的superDispatchTouchEvent方法來完成自己的工作,也就是說,當(dāng)前Activity的Window直接將這個(gè)touch事件傳遞給了DecorView。也就是說,目前touch事件已經(jīng)經(jīng)過了如下的分發(fā):Activity-->Window-->DecorView。

b. 頂級View對touch事件的分發(fā)

    經(jīng)過Activity與Window的分發(fā),現(xiàn)在touch事件已經(jīng)被傳遞到了DecorView的dispatchTouchEvent方法中。DecorView本質(zhì)上是一個(gè)ViewGroup(更具體的說是FrameLayout),ViewGroup的dispatchTouchEvent方法所做的工作可以分為如下幾個(gè)階段,第一個(gè)階段的主要代碼如下:

//Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
  //Throw away all previous state when starting a new touch gesture.
  //The framework may have dropped the up or cancel event for the previous gesture due to an app switch, ANR, or some other state change.
  cancelAndClearTouchTargets(ev);
  resetTouchState();
}

    第一階段的主要工作有倆:一是在第6行的resetTouchState方法中完成了對FLAG_DISALLOW_INTERCEPT標(biāo)記的重置;二是第5行的cancelAndClearTouchTargets方法會(huì)清除當(dāng)前MotionEvent的touch target。關(guān)于FLAG_DISALLOW_INTERCEPT標(biāo)記和touch target,在下文會(huì)有相關(guān)說明。

    第二階段的主要工作是決定當(dāng)前ViewGroup是否攔截本次的touch事件,主要代碼如下:

//Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWM || 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 {
  //There are no touch targets and this action is not an initial down so this view group continues to intercept touches.
  intercept =true;
}

    由以上代碼我們可以知道,當(dāng)一個(gè)touch事件被傳遞到ViewGroup時(shí),會(huì)先判斷這個(gè)touch事件的動(dòng)作是否是ACTION_DOWN,如果這個(gè)事件是ACTION_DOWN或者mFirstTouchTarget不為null,就會(huì)根據(jù)FLAG_DISALLOW_INTERCEPT標(biāo)記決定是否攔截這個(gè)touch事件。那么mFirstTouchTarget是什么呢?當(dāng)touch事件被ViewGroup的子View成功處理時(shí),mFirstTouchTarget就會(huì)被賦值為成功處理touch事件的View,也就是上面提高的touch target。

    總結(jié)一下上述代碼的流程:在子View不干預(yù)ViewGroup的攔截的情況下(上述代碼中的disallowIntercept為false),若當(dāng)前事件為ACTION_DOWN或者mFirstTouchTarget不為空,則會(huì)調(diào)用ViewGroup的onInterceptTouchEvent方法來決定最終是否攔截此事件;否則(沒有TargetView并且此事件不是ACTION_DOWN),當(dāng)前ViewGroup就攔截下此事件。 一旦ViewGroup攔截了某次touch事件,那么mFirstTouchTarget就不會(huì)被賦值,因此當(dāng)再有ACTION_MOVE或是ACTION_UP傳遞到該ViewGroup時(shí),mTouchTarget就為null,所以上述代碼第3行的條件就為false,ViewGroup會(huì)攔截下來。由此可得到的結(jié)論是:一旦ViewGroup攔截了某次事件,則同一事件序列中的剩余事件也會(huì)它默認(rèn)被攔截而不會(huì)再詢問是否攔截(即不會(huì)再調(diào)用onInterceptTouchEvent)。

    這里存在一種特殊情形,就是子View通過requestDisallowInterceptTouchEvent方法設(shè)置父容器的FLAG_DISALLOW_INTERCEPT為true,這個(gè)標(biāo)記指示是否不允許父容器攔截,為true表示不允許。這樣做能夠禁止父容器攔截除ACTION_DOWN以外的所有touch事件。之所以不能夠攔截ACTION_DOWN事件,是因?yàn)槊慨?dāng)ACTION_DOWN事件到來時(shí),都會(huì)重置FLAG_DISALLOW_INTERCEPT這個(gè)標(biāo)記位為默認(rèn)值(false),所以每當(dāng)開始一個(gè)新touch事件序列(即到來一個(gè)ACTION_DOWN動(dòng)作),都會(huì)通過調(diào)用onInterceptTouchEven詢問ViewGroup是否攔截此事件。當(dāng)ACTION_DOWN事件到來時(shí),重置標(biāo)記位的工作是在上面的第一階段完成的。  

    接下來,會(huì)進(jìn)入第三階段的工作:

final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL;
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
  // 不是ACTION_CANCEL并且不攔截
  if (actionMasked == MotionEvent.ACTION_DOWN) {
     // 若當(dāng)前事件為ACTION_DOWN則去尋找這次事件新出現(xiàn)的touch target
     final int actionIndex = ev.getActionIndex(); // always 0 for down

     ...

     final int childrenCount = mChildrenCount;
     if (newTouchTarget == null && childrenCount != 0) {
       // 根據(jù)觸摸的坐標(biāo)尋找能夠接收這個(gè)事件的touch target
       final float x = ev.getX(actionIndex);
       final float y = ev.getY(actionIndex);

       final View[] children = mChildren;
       // 遍歷所有子View
       for (int i = childrenCount - 1; i >= 0; i--) {
         final int childIndex = i;
         final View child = children[childIndex];
         // 尋找可接收這個(gè)事件并且touch事件坐標(biāo)在其區(qū)域內(nèi)的子View
         if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) {
           continue;
         }

         newTouchTarget = getTouchTarget(child); // 找到了符合條件的子View,賦值給newTouchTarget
         if (newTouchTarget != null) {
           //Child is already receiving touch within its bounds.
           //Give it the new pointer in addition to ones it is handling.
           newTouchTarget.pointerIdBits |= idBitsToAssign;
           break;
         }
         resetCancelNextUpFlag(child);
         // 把ACTION_DOWN事件傳遞給子組件進(jìn)行處理
         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();
           //把mFirstTouchTarget賦值為newTouchTarget,此子View成為新的touch事件的起點(diǎn)
           newTouchTarget = addTouchTarget(child, idBitsToAssign);
           alreadyDispatchedToNewTouchTarget = true;
           break;
         }           
       }
     }
  }
}

    當(dāng)ViewGroup不攔截本次事件,則touch事件會(huì)分發(fā)給它的子View進(jìn)行處理,相關(guān)代碼從第21行開始:遍歷所有ViewGroup的子View,尋找能夠處理此touch事件的子View,若一個(gè)子View不在播放動(dòng)畫并且touch事件坐標(biāo)位于其區(qū)域內(nèi),則該子View能夠處理此touch事件,并且會(huì)把該子View賦值給newTouchTarget。

    若當(dāng)前遍歷到的子View能夠處理此touch事件,就會(huì)進(jìn)入第38行的dispatchTransformedTouchEvent方法,該方法實(shí)際上調(diào)用了子View的dispatchTouchEvent方法。dispatchTransformedTouchEvent方法中相關(guān)的代碼如下:

if (child == null) {
  handled = super.dispatchTouchEvent(event);
} else {
  handled = child.dispatchTouchEvent(event);
}

    若dispatchTransformedTouchEvent方法傳入的child參數(shù)不為null,則會(huì)調(diào)用child(即處理touch事件的子View)的dispatchTouchEvent方法。若該子View的dispatchTouchEvent方法返回true,則dispatchTransformedTouchEvent方法也會(huì)返回true,則表示成功找到了一個(gè)處理該事件的touch target,會(huì)在第55行把newTouchTarget賦值給mFirstTouchTarget(這一賦值過程是在addTouchTarget方法內(nèi)部完成的),并跳出對子View遍歷的循環(huán)。若子View的dispatchTouchEvent方法返回false,ViewGroup就會(huì)把事件分發(fā)給下一個(gè)子View。

    若遍歷了所有子View后,touch事件都沒被處理(該ViewGroup沒有子View或是所有子View的dispatchTouchEvent返回false),ViewGroup會(huì)自己處理touch事件,相關(guān)代碼如下:

 if (mFirstTouchTarget == null) {
   handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS);
 }

    由以上代碼可知,ViewGroup自己處理touch事件時(shí),會(huì)調(diào)用dispatchTransformedTouchEvent方法,傳入的child參數(shù)為null。根據(jù)上文的分析,傳入的chid為null時(shí),會(huì)調(diào)用super.dispatchTouchEvent方法,即調(diào)用View類的dispatchTouchEvent方法。 

c. View對touch事件的處理

    View的dispatchTouchEvent方法的主要代碼如下:

public boolean dispatchTouchEvent(MotionEvent event) {
  boolean result = false;
  . . .
  
  if (onFilterTouchEventForSecurity(event)) {
    //noinspection SimplifiableIfStatement
    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對touch事件的處理過程如下:由于View不包含子元素,所以它只能自己處理事件。它首先會(huì)判斷是否設(shè)置了OnTouchListener,若設(shè)置了,會(huì)調(diào)用onTouch方法,若onTouch方法返回true(表示該touch事件已經(jīng)被消耗),則不會(huì)再調(diào)用onTouchEvent方法;若onTouch方法返回false或沒有設(shè)置OnTouchListener,則會(huì)調(diào)用onTouchEvent方法,onTouchEvent對touch事件進(jìn)行具體處理的相關(guān)代碼如下:

if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
  switch (event.getAction()) {
    case MotionEvent.ACTION_UP:
      boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
      if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
        . . .
        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) {
              mPerformClck = new PeformClick();
            }
            if (!post(mPerformClick)) {
              performClick();
            }
          }
        }
        . . .
      }
      break;
  }
  . . .
  return true;
}


    由以上代碼可知,只要View的CLICKABLE屬性和LONG_CLICKABLE屬性有一個(gè)為true(View的CLICKABLE屬性和具體View有關(guān),LONG_CLICKABLE屬性默認(rèn)為false,setOnClikListener和setOnLongClickListener會(huì)分別自動(dòng)將以上倆屬性設(shè)為true),那么這個(gè)View就會(huì)消耗這個(gè)touch事件,即使這個(gè)View處于DISABLED狀態(tài)。若當(dāng)前事件是ACTION_UP,還會(huì)調(diào)用performClick方法,該View若設(shè)置了OnClickListener,則performClick方法會(huì)在其內(nèi)部調(diào)用onClick方法。performClick方法代碼如下:

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;
}


 

以上是我學(xué)習(xí)Android中觸摸事件分發(fā)后的簡單總結(jié),很多地方敘述的還不夠清晰準(zhǔn)確

相關(guān)文章

最新評論