1. 程式人生 > >Android事件分發(3)--ViewGroup原始碼分析

Android事件分發(3)--ViewGroup原始碼分析

一、ViewGroup的onInterceptTouchEvent原始碼分析

onInterceptTouchEvent比較簡單先看他的原始碼

    public boolean onInterceptTouchEvent(MotionEvent ev) {
    //1、判斷是否是滑鼠裝置操作
    //2、ACTION_DOWN事件
    //3、是否是首要按鈕按下,如滑鼠左鍵
    //4、是否是滑動條     
    //看意思以上是相容安卓非手機裝置用的判斷,滿足的話,就返回true,攔截事件分發
        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
                && ev.getAction() == MotionEvent.ACTION_DOWN
                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
                && isOnScrollbarThumb(ev.getX(), ev.getY())) {
            return
true; } //預設返回false不攔截事件分發 return false; }

onInterceptTouchEvent 作用就是要不要攔截向子View分發事件,如果想攔截的話,需要重寫onInterceptTouchEvent。

二、ViewGroup的dispatchTouchEvent原始碼分析

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
    //如果輸入一致性檢查事件不為null,先執行輸入前後一致性檢查
    //原始碼英文註釋說是為了debug的目的,估計是設定斷點後,再次執行的時候,為了不丟失事件,檢查是否是同一個輸入,可忽略
if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } // If the event targets the accessibility focused view and this is it, start // normal event dispatch. Maybe a descendant is what will handle the click. //如果事件物件是容易獲取焦點的view,開始普通事件分發,可能是某個後代會處理點選。
//貌似是用來判斷 motion event是否設定了容易獲取焦點Flags //這個flags是FLAG是_WINDOW_IS_OBSCURED,原始碼意思大約是設定事件物件,是否部分或者全部被遮擋到了 if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) { ev.setTargetAccessibilityFocus(false); } //handled 是最終要返回的結果值 boolean handled = false; //onFilterTouchEventForSecurity 對事件進行安全檢查,如果true可執行,false就放棄 //onFilterTouchEventForSecurity 裡面進行了是否要遮擋進行過濾,以及event的物件是否被遮擋的兩個判斷 if (onFilterTouchEventForSecurity(ev)) { //未被遮擋進入了此if內部 final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; // Handle an initial down. //處理最初的ACTION_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. //當開始了一個新的手勢,拋棄了之前所有的狀態 //由於app切換,ANR或者其他狀態改變,框架可能已經放棄了對前一個手勢的Up 和cancel的處理 //cancelAndClearTouchTargets 清空了之前所有的找到的觸控物件,裡面呼叫了dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);對子View的事件進行了取消,傳遞的第二個引數true 是是否取消的標誌 //裡面呼叫了clearTouchTargets 對比較重要的mFirstTouchTarget物件賦值null //mFirstTouchTarget的英文註釋 First touch target in the linked list of touch targets.是第一個找到的觸控物件 cancelAndClearTouchTargets(ev); resetTouchState(); } //檢查是否攔截 // Check for interception. final boolean intercepted; //第一種情況ACTION_DOWN的時候判斷攔截, //第二種情況,已經找到了要處理事件的字View或者子ViewGroup if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { //之後又對disallowIntercept進行了判斷,disallowIntercept的意思是,是否禁用攔截,disallowIntercept可以使用requestDisallowInterceptTouchEvent方法自己進行設定,後面會具體分析 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { //沒有禁用攔截,就會根據攔截函式onInterceptTouchEvent的返回值,進行攔截判斷,是否攔截 intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed恢復action,防止action被改變了 } 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. //沒有找到處理MotionEvent的目標物件,而且也不是最初的ACTION_DOWN,也就是現在是up 或者move事件了 //mFirstTouchTarget 為null說明自己沒有需要處理事件的物件,也就是自己這個ViewGroup下沒有View要處理事件,所以攔截 // intercepted = true; } // If intercepted, start normal event dispatch. Also if there is already // a view that is handling the gesture, do normal event dispatch. if (intercepted || mFirstTouchTarget != null) { ev.setTargetAccessibilityFocus(false); } // Check for cancelation. //檢查事件是否被取消 final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; // Update list of touch targets for pointer down, if needed. final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0; //定義了一個新的觸控物件,下面會用到 TouchTarget newTouchTarget = null; boolean alreadyDispatchedToNewTouchTarget = false; if (!canceled && !intercepted) {//沒被取消或攔截 // If the event is targeting accessiiblity focus we give it to the // view that has accessibility focus and if it does not handle it // we clear the flag and dispatch the event to all children as usual. // We are looking up the accessibility focused host to avoid keeping // state since these events are very rare. View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus() : null; //ACTION_POINTER_DOWN 是說在有一個觸控點的情況下,又有手指觸控式螢幕幕。多點觸控的判斷 //主要關心ACTION_DOWN事件就可以了, 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 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) : TouchTarget.ALL_POINTER_IDS; // Clean up earlier touch targets for this pointer id in case they // have become out of sync. removePointersFromTouchTargets(idBitsToAssign); //有效的子View的數量 final int childrenCount = mChildrenCount; //newTouchTarget 為null且子view數量不為0 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. //找到一個能接收事件的子View,檢視層次上從上層到下層的順序查詢 final ArrayList<View> preorderedList = buildTouchDispatchChildList(); final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); final View[] children = mChildren; //是子View倒序遍歷子View,因為在新增子view到group的過程中,先新增的顯示在底層,後新增的顯示在上層,所以查詢的時候,先從可見的上層查詢起 for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = getAndVerifyPreorderedIndex( childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView( preorderedList, children, 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; } //判斷子View能不能收到觸控事件 //判斷自VIew是不是在觸控範圍內 如果不滿足,就不檢查了 if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; } //子View滿足可觸控和在觸控範圍內 //找到某一指定的子view的TouchTarget物件,傳遞引數有子View,返回子VIew的TouchTarget物件 newTouchTarget = getTouchTarget(child); //沒有找到子View的TouchTarget物件的處理 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); //把經過一定改變的MotionEvent傳遞給子View的相對應的區域,是在這個方法裡,進行的子View的分發事件的呼叫。稍後單獨分析。 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. //返回true表示子View想處理消費事件, mLastTouchDownTime = ev.getDownTime(); //找到消費事件的view的index 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(); //把要消費事件的子View的TouchTarget物件放到list開頭 //addTouchTarget 裡面對mFirstTouchTarget進行了賦值,並返回,mFirstTouchTarget和newTouchTarget 此時指向相同 newTouchTarget = addTouchTarget(child, idBitsToAssign); //已經分發標識設定為true 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(); } //沒有找到消費事件的子View if (newTouchTarget == null && mFirstTouchTarget != null) { // Did not find a child to receive the event. // Assign the pointer to the least recently added target. newTouchTarget = mFirstTouchTarget; while (newTouchTarget.next != null) { newTouchTarget = newTouchTarget.next; } newTouchTarget.pointerIdBits |= idBitsToAssign; } } } //以上是對 不攔截,未取消事件的處理和分發 ,如果找到了要消費事件的view, 那麼mFirstTouchTarget 就不應該為空了 //mFirstTouchTarget 為null 一是可能事件都被攔截了,二是沒有找到要消費事件的子View // Dispatch to touch targets. if (mFirstTouchTarget == null) { // No touch targets so treat this as an ordinary view. //沒有觸控物件,把它當成原始的view來處理 //第三個引數傳遞是null,在裡面判斷了null會呼叫view的dispatchTouchEvent方法,也就是把ViewGroup當成view處理 handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); } else { //有子View消費事件 // // Dispatch to touch targets, excluding the new touch target if we already // dispatched to it. Cancel touch targets if necessary. TouchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; while (target != null) { final TouchTarget next = target.next; //alreadyDispatchedToNewTouchTarget為true是在找到newTouchTarget的時候設定的 //且alreadyDispatchedToNewTouchTarget設定為true是在ACTION_DOWN的if判斷裡面的,它消費了ACTION_DOWN事件 //所以返回handled = true表示已經有子view消費事件了 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; } else { // 對於非ACTION_DOWN事件,則繼續傳遞給目標子元件進行處理(注意這裡的非ACTION_DOWN事件已經不需要再判斷是否攔截) final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { // 如果target子元件進行處理,符合某些條件的話,會傳遞ACTION_CANCEL給target子元件 // 條件是:如果ACTION_DOWN時沒有被攔截,而後面的touch事件被攔截,則需要傳送ACTION_CANCEL給target子元件 handled = true; } if (cancelChild) { if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; } target.recycle(); target = next; continue; } } predecessor = target; target = next; } } // Update list of touch targets for pointer up or cancel, if needed. //ACTION_CANCEL或者ACTION_UP,重置touch 的狀態 //mFirstTouchTarget重置為null if (canceled || actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { resetTouchState(); } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) { //多點觸控的取消處理 final int actionIndex = ev.getActionIndex(); final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); removePointersFromTouchTargets(idBitsToRemove); } } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1); } return handled; }

相關推薦

Android事件分發3--ViewGroup原始碼分析

一、ViewGroup的onInterceptTouchEvent原始碼分析 onInterceptTouchEvent比較簡單先看他的原始碼 public boolean onInterceptTouchEvent(MotionEvent ev

android事件分發

sim tdi p s oat front rac ram addclass framework 非常早之前寫過一篇android事件分發的博客,主要寫的是它是怎樣分發的,具體非常多原理的東西都沒有涉及到。今天就從源代碼看android怎樣控制它的分發機

android事件分發重要的函式requestDisallowInterceptTouchEvent

事件分發在android中非常重要,寫了3篇文章總結其中的故事 具體實現 前面我們說過,兒子吃到肉了,父親還可能搶那麼兒子有沒有辦法不讓父親搶呢,有? 可以通過呼叫mParent.requestDisallowInterceptTouchEvent(true),

android原始碼分析之View的事件分發

1、View的繼承關係圖 View的繼承關係圖如下: 其中最重要的子類為ViewGroup,View是所有UI元件的基類,而ViewGroup是容納這些元件的容器,同時它也是繼承於View類。而UI元件的繼承關係如上圖,比較常用的元件類用紅色字型標出

【朝花夕拾】Android自定義View篇之Android事件分發機制原始碼分析事件分發邏輯及經常遇到的一些“詭異”現象

前言        轉載請註明,轉自【https://www.cnblogs.com/andy-songwei/p/11039252.html】謝謝!        在上一篇文章【【朝花夕拾】Android自定義View篇之(

Android事件分發dispatchTouchEvent,攔截onInterceptTouchEvent與處理onTouchEvent

在Android中,View的結構是樹狀的,所以,當觸發觸控事件的時候,其事件傳遞也是從上之下一層層的傳遞。下面我們結合例子來一點點進行分析。 首先,我們需要了解事件處理中的幾個方法: 1、在ViewGroup中,事件分為dispatchTouchEvent(事件的分發)

cocos2dx學習原始碼之介面iOS事件分發2

今天看看事件是如何分發的。程式碼版本:cocos2dx-3.9 上次說到GLView接手touch事件:handleTouchesBegin。 原始碼: void GLView::handleTou

Android touch 事件分發 Activity dispatchTouchEvent

/** * You can override this to intercept all touch screen events before they are dispatched to the window <br> * * @return f

GeoMesa編譯與二次開發專欄3 — GeoMesa原始碼編譯

前言 1、參考:GeoMesa官方英文文件:https://www.geomesa.org/documentation/developer/introduction.html 2、本篇介紹瞭如何編譯GeoMesa原始碼,即官方文件中所謂的從原始碼構建GeoMesa,以及對GeoM

android影象處理3浮雕效果

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

android影象處理3底片效果

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

android四大元件3(BroadcastReceiver)

一、aandroid的四大元件BroadcastReceiver廣播接收器(用於接收程式所發出的Broadcast Intent),其本生就是一個全域性的監聽器,用於監聽系統全域性的廣播訊息。由於BroadcastReceiver是全域性的監聽器,所以它可以非常方便地實現系統中不同元件之間地通訊。

Android事件匯流排EventBus3.0用法全解析

前言 EventBus是一款針對Android優化的釋出/訂閱事件匯流排。簡化了應用程式內各元件間、元件與後臺執行緒間的通訊。優點是開銷小,程式碼更優雅,以及將傳送者和接收者解耦。如果Activity和Activity進行互動還好說,如果Fragmen

寫給Android App開發人員看的Android底層知識3

       (七)App啟動流程第2篇        書接上文,App啟動一共有七個階段,上篇文章篇幅所限,我們只看了第一階段,接下來講剩餘的六個階段,仍然是拿鬥魚App舉例子。        簡單回顧一下第一階段的流程,就是Launcher向AMS傳送一個跨程序通訊,通過AMN/AMP,告訴A

android-進階3-自定義view(2)-Android中View繪製流程以及相關方法的分析

最近正在學自定義view,這篇文章主要講view的繪製流程和一些相關的方法,淺顯易懂,寫的非常好,忍不住就轉載了。             前言: 本文是我讀《Android核心剖析》第13章----View工作原理總結而成的,在此膜拜下作者 。

WSO2 ——3ESB 原始碼編譯

WSO2 ESB編譯真是費勁,編譯得花費四五個小時。先是編譯4.8.0版本,缺少各種jar包轉戰到4.7.0版本,最後發現原來是公司網路原因,崩潰。4.7.0版本網上有人已經編譯成功,又花了幾天時間終於編譯完成了。 1  幾個概念 1.1 WSO2 ESB wso2的一個產

Android framework回顧3binder利用及IBinder BpRefbase IInterface INTERFACE 之間關係

status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream, int index, audio_devices_t device){     const sp<IAudioPolicySe

android-進階3-自定義view(1)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"     xmlns:custom="http://sch

HLS學習HLSDownloader原始碼分析3總體流程

總體流程 下載一個媒體檔案的流程 1、初始化 2、根據URL下載m3u8檔案(即PlayList檔案) 3、判斷m3u8檔案的型別,是Master PlayList還是Media PlayList 4、如果是Master PlayList,那麼分析這個PlayList

android launcher開發3初始化介面

初始化執行環境 LauncherAppState.setApplicationContext(getApplicationContext()); LauncherAppState app = LauncherAppState.getIns