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

Android事件分發(fā)機(jī)制(上) ViewGroup的事件分發(fā)

 更新時(shí)間:2017年01月16日 10:49:11   作者:無(wú)嘴小呆子  
這篇文章主要為大家詳細(xì)介紹了Android ViewGroup的事件分發(fā)機(jī)制上篇,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

綜述

  Android中的事件分發(fā)機(jī)制也就是View與ViewGroup的對(duì)事件的分發(fā)與處理。在ViewGroup的內(nèi)部包含了許多View,而ViewGroup繼承自View,所以ViewGroup本身也是一個(gè)View。對(duì)于事件可以通過(guò)ViewGroup下發(fā)到它的子View并交由子View進(jìn)行處理,而ViewGroup本身也能夠?qū)κ录龀鎏幚怼O旅婢蛠?lái)詳細(xì)分析一下ViewGroup對(duì)時(shí)間的分發(fā)處理。

MotionEvent

  當(dāng)手指接觸到屏幕以后,所產(chǎn)生的一系列的事件中,都是由以下三種事件類型組成。
  1. ACTION_DOWN: 手指按下屏幕
  2. ACTION_MOVE: 手指在屏幕上移動(dòng)
  3. ACTION_UP: 手指從屏幕上抬起
  例如一個(gè)簡(jiǎn)單的屏幕觸摸動(dòng)作觸發(fā)了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->…->ACTION_MOVE->ACTION_UP
  對(duì)于Android中的這個(gè)事件分發(fā)機(jī)制,其中的這個(gè)事件指的就是MotionEvent。而View的對(duì)事件的分發(fā)也是對(duì)MotionEvent的分發(fā)操作??梢酝ㄟ^(guò)getRawX和getRawY來(lái)獲取事件相對(duì)于屏幕左上角的橫縱坐標(biāo)。通過(guò)getX()和getY()來(lái)獲取事件相對(duì)于當(dāng)前View左上角的橫縱坐標(biāo)。

三個(gè)重要方法

public boolean dispatchTouchEvent(MotionEvent ev)

  這是一個(gè)對(duì)事件分發(fā)的方法。如果一個(gè)事件傳遞給了當(dāng)前的View,那么當(dāng)前View一定會(huì)調(diào)用該方法。對(duì)于dispatchTouchEvent的返回類型是boolean類型的,返回結(jié)果表示是否消耗了這個(gè)事件,如果返回的是true,就表明了這個(gè)View已經(jīng)被消耗,不會(huì)再繼續(xù)向下傳遞?! ?

public boolean onInterceptTouchEvent(MotionEvent ev)

  該方法存在于ViewGroup類中,對(duì)于View類并無(wú)此方法。表示是否攔截某個(gè)事件,ViewGroup如果成功攔截某個(gè)事件,那么這個(gè)事件就不在向下進(jìn)行傳遞。對(duì)于同一個(gè)事件序列當(dāng)中,當(dāng)前View若是成功攔截該事件,那么對(duì)于后面的一系列事件不會(huì)再次調(diào)用該方法。返回的結(jié)果表示是否攔截當(dāng)前事件,默認(rèn)返回false。由于一個(gè)View它已經(jīng)處于最底層,它不會(huì)存在子控件,所以無(wú)該方法。   

public boolean onTouchEvent(MotionEvent event)

  這個(gè)方法被dispatchTouchEvent調(diào)用,用來(lái)處理事件,對(duì)于返回的結(jié)果用來(lái)表示是否消耗掉當(dāng)前事件。如果不消耗當(dāng)前事件的話,那么對(duì)于在同一個(gè)事件序列當(dāng)中,當(dāng)前View就不會(huì)再次接收到事件。   

View事件分發(fā)流程圖

  對(duì)于事件的分發(fā),在這里先通過(guò)一個(gè)流程圖來(lái)看一下整個(gè)分發(fā)過(guò)程。

 

ViewGroup事件分發(fā)源碼分析

  根據(jù)上面的流程圖現(xiàn)在就詳細(xì)的來(lái)分析一下ViewGroup事件分發(fā)的整個(gè)過(guò)程。
  手指在觸摸屏上滑動(dòng)所產(chǎn)生的一系列事件,當(dāng)Activity接收到這些事件通過(guò)調(diào)用Activity的dispatchTouchEvent方法來(lái)進(jìn)行對(duì)事件的分發(fā)操作。下面就來(lái)看一下Activity的dispatchTouchEvent方法。

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

  通過(guò)getWindow().superDispatchTouchEvent(ev)這個(gè)方法可以看出來(lái),這個(gè)時(shí)候Activity又會(huì)將事件交由Window處理。Window它是一個(gè)抽象類,它的具體實(shí)現(xiàn)只有一個(gè)PhoneWindow,也就是說(shuō)這個(gè)時(shí)候,Activity將事件交由PhoneWindow中的superDispatchTouchEvent方法。現(xiàn)在跟蹤進(jìn)去看一下這個(gè)superDispatchTouchEvent代碼。

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

  這里面的mDecor它是一個(gè)DecorView,DecorView它是一個(gè)Activity的頂級(jí)View。它是PhoneWindow的一個(gè)內(nèi)部類,繼承自FrameLayout。于是在這個(gè)時(shí)候事件又交由DecorView的superDispatchTouchEvent方法來(lái)處理。下面就來(lái)看一下這個(gè)superDispatchTouchEvent方法。

public boolean superDispatchTouchEvent(MotionEvent event) {
 return super.dispatchTouchEvent(event);
}

  在這個(gè)時(shí)候就能夠很清晰的看到DecorView它調(diào)用了父類的dispatchTouchEvent方法。在上面說(shuō)到DecorView它繼承了FrameLayout,而這個(gè)FrameLayout又繼承自ViewGroup。所以在這個(gè)時(shí)候事件就開始交給了ViewGroup進(jìn)行處理了。下面就開始詳細(xì)看下這個(gè)ViewGroup的dispatchTouchEvent方法。由于dispatchTouchEvent代碼比較長(zhǎng),在這里就摘取部分代碼進(jìn)行說(shuō)明。

// 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();
}

  從上面代碼可以看出,在dispatchTouchEvent中,會(huì)對(duì)接收的事件進(jìn)行判斷,當(dāng)接收到的是ACTION_DOWN事件時(shí),便會(huì)清空事件分發(fā)的目標(biāo)和狀態(tài)。然后執(zhí)行resetTouchState方法重置了觸摸狀態(tài)。下面就來(lái)看一下這兩個(gè)方法。

1. cancelAndClearTouchTargets(ev)

private TouchTarget mFirstTouchTarget;

......

private void cancelAndClearTouchTargets(MotionEvent event) {
 if (mFirstTouchTarget != null) {
 boolean syntheticEvent = false;
 if (event == null) {
  final long now = SystemClock.uptimeMillis();
  event = MotionEvent.obtain(now, now,
   MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
  event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
  syntheticEvent = true;
 }

 for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
  resetCancelNextUpFlag(target.child);
  dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
 }
 clearTouchTargets();

 if (syntheticEvent) {
  event.recycle();
 }
 }
}

  在這里先介紹一下mFirstTouchTarget,它是TouchTarget對(duì)象,TouchTarget是ViewGroup的一個(gè)內(nèi)部類,TouchTarget采用鏈表數(shù)據(jù)結(jié)構(gòu)進(jìn)行存儲(chǔ)View。而在這個(gè)方法中主要的作用就是清空mFirstTouchTarget鏈表并將mFirstTouchTarget設(shè)為空。

2. resetTouchState()

private void resetTouchState() {
 clearTouchTargets();
 resetCancelNextUpFlag(this);
 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
 mNestedScrollAxes = SCROLL_AXIS_NONE;
}

  在這里介紹一下FLAG_DISALLOW_INTERCEPT標(biāo)記,這是禁止ViewGroup攔截事件的標(biāo)記,可以通過(guò)requestDisallowInterceptTouchEvent方法來(lái)設(shè)置這個(gè)標(biāo)記,當(dāng)設(shè)置了這個(gè)標(biāo)記以后,ViewGroup便無(wú)法攔截除了ACTION_DOWN以外的其它事件。因?yàn)樵谏厦娲a中可以看出,當(dāng)事件為ACTION_DOWN時(shí),會(huì)重置FLAG_DISALLOW_INTERCEPT標(biāo)記。
  那么下面就再次回到dispatchTouchEvent方法中繼續(xù)看它的源代碼。

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

  這段代碼主要就是ViewGroup對(duì)事件是否需要攔截進(jìn)行的判斷。下面先對(duì)mFirstTouchTarget是否為null這兩種情況進(jìn)行說(shuō)明。當(dāng)事件沒(méi)有被攔截時(shí),ViewGroup的子元素成功處理事件后,mFirstTouchTarget會(huì)被賦值并且指向其子元素。也就是說(shuō)這個(gè)時(shí)候mFirstTouchTarget!=null??墒且坏┦录粩r截,mFirstTouchTarget不會(huì)被賦值,mFirstTouchTarget也就為null。
  在上面代碼中可以看到根據(jù)actionMasked==MotionEvent.ACTION_DOWN||mFirstTouchTarget!=null這兩個(gè)情況進(jìn)行判斷事件是否需要攔截。對(duì)于actionMasked==MotionEvent.ACTION_DOWN這個(gè)條件很好理解,對(duì)于mFirstTouchTarget!=null的兩種情況上面已經(jīng)說(shuō)明。那么對(duì)于一個(gè)事件序列,當(dāng)事件為MotionEvent.ACTION_DOWN時(shí),會(huì)重置FLAG_DISALLOW_INTERCEPT,也就是說(shuō)!disallowIntercept一定為true,必然會(huì)執(zhí)行onInterceptTouchEvent方法,對(duì)于onInterceptTouchEvent方法默認(rèn)返回為false,所以需要ViewGroup攔截事件時(shí),必須重寫onInterceptTouchEvent方法,并返回true。這里有一點(diǎn)需要注意,對(duì)于一個(gè)事件序列,一旦序列中的某一個(gè)事件被成功攔截,執(zhí)行了onInterceptTouchEvent方法,也就是說(shuō)onInterceptTouchEvent返回值為true,那么該事件之后一系列事件對(duì)于條件actionMasked==MotionEvent.ACTION_DOWN||mFirstTouchTarget!=null必然為false,那么這個(gè)時(shí)候該事件序列剩下的一系列事件將會(huì)被攔截,并且不會(huì)執(zhí)行onInterceptTouchEvent方法。于是在這里得出一個(gè)結(jié)論:對(duì)于一個(gè)事件序列,當(dāng)其中某一個(gè)事件成功攔截時(shí),那么對(duì)于剩下的一系列事件也會(huì)被攔截,并且不會(huì)再次執(zhí)行onInterceptTouchEvent方法
  下面再來(lái)看一下對(duì)于ViewGroup并沒(méi)有攔截事件是如何進(jìn)行處理的。

final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
 final float x = ev.getX(actionIndex);
 final float y = ev.getY(actionIndex);
 // Find a child that can receive the event.
 // Scan children from front to back.
 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);

 // If there is a view that has accessibility focus we want it
 // to get the event first and if not handled we will perform a
 // normal dispatch. We may do a double iteration but this is
 // safer given the timeframe.
 if (childWithAccessibilityFocus != null) {
  if (childWithAccessibilityFocus != child) {
  continue;
  }
  childWithAccessibilityFocus = null;
  i = childrenCount - 1;
 }

 if (!canViewReceivePointerEvents(child)
  || !isTransformedTouchPointInView(x, y, child, null)) {
  ev.setTargetAccessibilityFocus(false);
  continue;
 }

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

 resetCancelNextUpFlag(child);
 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;
 }

 // The accessibility focus didn't handle the event, so clear
 // the flag and do a normal dispatch to all children.
 ev.setTargetAccessibilityFocus(false);
 }
 if (preorderedList != null) preorderedList.clear();
}

  對(duì)于這段代碼雖然說(shuō)比較長(zhǎng),但是在這里面的邏輯去不是很復(fù)雜。首先獲取當(dāng)前ViewGroup中的子View和ViewGroup的數(shù)量。然后對(duì)該ViewGroup中的元素進(jìn)行逐步遍歷。在獲取到ViewGroup中的子元素后,判斷該元素是否能夠接收觸摸事件。子元素若是能夠接收觸摸事件,并且該觸摸坐標(biāo)在子元素的可視范圍內(nèi)的話,便繼續(xù)向下執(zhí)行。否則就continue。對(duì)于衡量子元素能否接收到觸摸事件的標(biāo)準(zhǔn)有兩個(gè):子元素是否在播放動(dòng)畫和點(diǎn)擊事件的坐標(biāo)是否在子元素的區(qū)域內(nèi)。
  一旦子View接收到了觸摸事件,然后便開始調(diào)用dispatchTransformedTouchEvent方法對(duì)事件進(jìn)行分發(fā)處理。對(duì)于dispatchTransformedTouchEvent方法代碼比較多,現(xiàn)在只關(guān)注下面這五行代碼。從下面5行代碼中可以看出,這時(shí)候會(huì)調(diào)用子View的dispatchTouchEvent,也就是在這個(gè)時(shí)候ViewGroup已經(jīng)完成了事件分發(fā)的整個(gè)過(guò)程。

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

  當(dāng)子元素的dispatchTouchEvent返回為true的時(shí)候,也就是子View對(duì)事件處理成功。這時(shí)候便會(huì)通過(guò)addTouchTarget方法對(duì)mFirstTouchTarget進(jìn)行賦值。
  如果dispatchTouchEvent返回了false,或者說(shuō)當(dāng)前的ViewGroup沒(méi)有子元素的話,那么這個(gè)時(shí)候便會(huì)調(diào)用如下代碼。

if (mFirstTouchTarget == null) {
 // No touch targets so treat this as an ordinary view.
 handled = dispatchTransformedTouchEvent(ev, canceled, null,
  TouchTarget.ALL_POINTER_IDS);
}

  在這里調(diào)用dispatchTransformedTouchEvent方法,并將child參數(shù)設(shè)為null。也就是執(zhí)行了super.dispatchTouchEvent(event)方法。由于ViewGroup繼承自View,所以這個(gè)時(shí)候又將事件交由父類的dispatchTouchEvent進(jìn)行處理。對(duì)于父類View是如何通過(guò)dispatchTouchEvent對(duì)事件進(jìn)行處理的,在下篇文章中會(huì)進(jìn)行詳細(xì)說(shuō)明。
  到這里對(duì)于ViewGroup的事件分發(fā)已經(jīng)講完了,在這一路下來(lái),不難發(fā)現(xiàn)對(duì)于dispatchTouchEvent有一個(gè)boolean類型返回值。對(duì)于這個(gè)返回值,當(dāng)返回true的時(shí)候表示當(dāng)前事件處理成功,若是返回false,一般來(lái)說(shuō)是因?yàn)樵谑录幚韔nTouchEvent返回了false,這時(shí)候變會(huì)交由它的父控件進(jìn)行處理,以此類推,若是一直處理失敗,則最終會(huì)交由Activity的onTouchEvent方法進(jìn)行處理。

總結(jié)

  在這里從宏觀上再看一下這個(gè)ViewGroup對(duì)事件的分發(fā),當(dāng)ViewGroup接收一個(gè)事件序列以后,首先會(huì)判斷是否攔截該事件,若是攔截該事件,則通過(guò)調(diào)用父類View的dispatchTouchEvent來(lái)處理這個(gè)事件。若是不去攔截這一事件,便將該事件下發(fā)到子View當(dāng)中。若果說(shuō)ViewGroup沒(méi)有子View,或者說(shuō)子View對(duì)事件處理失敗,則將該事件有交由該ViewGroup處理,若是該ViewGroup對(duì)事件依然處理失敗,最終則會(huì)將事件交由Activity進(jìn)行處理。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Android優(yōu)化方案之Fragment的懶加載實(shí)現(xiàn)代碼

    Android優(yōu)化方案之Fragment的懶加載實(shí)現(xiàn)代碼

    本篇文章主要介紹了Android優(yōu)化方案之Fragment的懶加載實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • 淺談Okhttp去除請(qǐng)求頭user-agent

    淺談Okhttp去除請(qǐng)求頭user-agent

    本篇文章主要介紹了淺談Okhttp去除請(qǐng)求頭user-agent,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • Android中3種圖片壓縮處理方法

    Android中3種圖片壓縮處理方法

    這篇文章主要介紹了Android中3種圖片壓縮處理方法,本文講解了質(zhì)量壓縮方法、獲得縮略圖、圖片縮放三種方法并分別給出示例代碼,需要的朋友可以參考下
    2015-06-06
  • Android學(xué)習(xí)小結(jié)之獲取被啟動(dòng)的Activity傳回的數(shù)據(jù)

    Android學(xué)習(xí)小結(jié)之獲取被啟動(dòng)的Activity傳回的數(shù)據(jù)

    這篇文章主要介紹了獲取被啟動(dòng)的Activity傳回的數(shù)據(jù),非常不錯(cuò),介紹的非常詳細(xì),需要的朋友可以參考下
    2016-08-08
  • Android?AccessibilityService?事件分發(fā)原理分析總結(jié)

    Android?AccessibilityService?事件分發(fā)原理分析總結(jié)

    這篇文章主要介紹了Android?AccessibilityService?事件分發(fā)原理分析總結(jié),AccessibilityService有很多用來(lái)接收外部調(diào)用事件變化的方法,這些方法封裝在內(nèi)部接口Callbacks中,文章圍繞AccessibilityService相關(guān)資料展開詳情,需要的朋友可以參考一下
    2022-06-06
  • Android中貝塞爾曲線的繪制方法示例代碼

    Android中貝塞爾曲線的繪制方法示例代碼

    本文主要介紹Android中貝塞爾曲線的繪制方法,這里詳細(xì)介紹了如何繪制貝塞爾曲線,并附有示例代碼,有需要的小伙伴可以參考下
    2016-08-08
  • Android編程實(shí)現(xiàn)手機(jī)震動(dòng)功能的方法

    Android編程實(shí)現(xiàn)手機(jī)震動(dòng)功能的方法

    這篇文章主要介紹了Android編程實(shí)現(xiàn)手機(jī)震動(dòng)功能的方法,結(jié)合實(shí)例形式分析了Android實(shí)現(xiàn)手機(jī)震動(dòng)功能的核心代碼與權(quán)限控制操作技巧,需要的朋友可以參考下
    2017-06-06
  • android ListView深入理解

    android ListView深入理解

    在android開發(fā)中ListView是比較常用的組件,它以列表的形式展示具體內(nèi)容,并且能夠根據(jù)數(shù)據(jù)的長(zhǎng)度自適應(yīng)顯示。抽空把對(duì)ListView的使用做了整理,需要的朋友可以參考下
    2012-11-11
  • popupwindow焦點(diǎn)問(wèn)題解決方案

    popupwindow焦點(diǎn)問(wèn)題解決方案

    在android 開發(fā)過(guò)程中,總會(huì)遇到一些問(wèn)題,比如popupwindow焦點(diǎn)問(wèn)題等等,我們?cè)撊绾谓鉀Q呢?需要的朋友可以了解下
    2012-11-11
  • Android中post請(qǐng)求傳遞json數(shù)據(jù)給服務(wù)端的實(shí)例

    Android中post請(qǐng)求傳遞json數(shù)據(jù)給服務(wù)端的實(shí)例

    下面小編就為大家分享一篇Android中post請(qǐng)求傳遞json數(shù)據(jù)給服務(wù)端的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01

最新評(píng)論

桐乡市| 新绛县| 乐都县| 普格县| 翼城县| 雷州市| 盐源县| 深州市| 扎赉特旗| 从江县| 青铜峡市| 塔河县| 永定县| 沈丘县| 登封市| 大同市| 大厂| 镇江市| 右玉县| 旌德县| 瑞安市| 孟津县| 翼城县| 荃湾区| 晴隆县| 通山县| 新昌县| 普陀区| 玛纳斯县| 斗六市| 江孜县| 宜城市| 长岭县| 阳新县| 昌吉市| 洛宁县| 米泉市| 海宁市| 息烽县| 汽车| 铜陵市|