<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] ![](https://img.kancloud.cn/0c/bb/0cbb39141c0d7e0c72c72f8263f90a49_2190x976.png) ## 事件分發如何通過遞歸實現(dispatchTouchEvent) 遞歸分為遞過程和歸過程。 ![](https://img.kancloud.cn/0a/12/0a121156904f49a2905d897d6952183f_624x859.png) 當 UI 事件來臨時,它首先被分發到 activity,activity 分發給 window,window 再分發給 view 樹。分發過程 **通過 dispatchTouchEvent 函數完成。它的返回值是 Boolean,如果為 true,表示事件被消費;為 false,表示事件未被消費。** dispatchTouchEvent 在不同的分發階段有不同的作用 對于 View 而言,它沒有子 View,**View 自己的 dispatchTouchEvent 實際上會執行到 onTouchEvent** 來進行事件的消費 ![](https://img.kancloud.cn/20/56/2056958795824cf83ee0899b0f037b7b_1256x932.png) 對于 ViewGroup 而言,它重寫了 dispatchTouchEvent, 先將事件分發給自己的子 View。 如果子 View 可以消費事件, dispatchTouchEvent 直接返回 true;如果子 View 都不消費事件,就調用自己的 onTouchEvent 來進行事件的消費。**(ViewGroup 的 onTouchEvent 實際上就是 super.dispatchTouchEvent)** ![](https://img.kancloud.cn/08/75/08753c8dd12310624204b0010f472814_1248x774.png) 這樣,Activity 通過調用 Window#superDispatchTouchEvent 方法將事件分發給 Window,Window 調用 DecorView#dispatchTouchEvent 將事件分發給 decorView。DecorView 是一個 ViewGroup,它又將事件分發給子 ViewGroup 和子 View。當有一個 View 消費掉事件時,他就會向上返回,通過遞歸鏈返回到 Activity#dispatchTouchEvent。最終完成事件的分發,消費和返回。 ## 如何避免每一個事件的分發都需要遞歸嗎? ViewGroup 里面用了一個成員變量 mFirstTouchTarget 來保存消費事件的子 View 信息,因為安卓是支持多指操作的,所以這個 mFirstTouchTarget 是一個 TouchTarget 的鏈表。在View 的 dispatchTouchEvent 可以分為三個階段:判斷是否需要攔截; 分發事件找到消費事件的子 View,更新到 mFirstTouchTarget;根據是否攔截和 mFirstTouchTarget 再次分發事件 所以 Android 為了避免每個事件都遞歸遍歷,定義了一個 **【事件序列】** 的概念:將用戶每一次觸摸屏幕 --> 移動屏幕-->抬起手指稱為一個事件序列。 一個事件序列必然包含 ACTION\_DOWN,ACTION\_MOVE,ACTION\_UP 等多個事件。其中 ACTION\_MOVE 數量不確定,ACTION\_DOWN 和 ACTION\_UP 數量則為 1 當接收到 ACTION\_DOMN 事件時,意味著一次完成事件序列的開始。ViewGroup 會通過遞歸遍歷找到 View 樹中真正對事件進行消費的子 View,并將其保存。這之后接收到 ACTION\_MOVE 和 ACTION\_DOWN 事件時,則跳過遞歸遍歷的過程,直接交給之前保存的消費者。當下一次 ACTION\_DOWN 事件來臨時重置整個過程 ![](https://img.kancloud.cn/6d/1a/6d1a9ed0d8c1d0babfb57d3e76d8dc40_1252x1128.png) ## 如何在事件被分發前攔截 > 嗯,了解的還挺仔細的嘛。那如果有一個 view 可以消費事件,但我想在事件分發給它之前進行攔截,該怎么做? ViewGroup 提供了一個攔截事件的函數**onInterceptTouchEvent,返回值為 Boolean。** 表示是否攔截事件。如果攔截,則會調用 onTouchEvent 進行事件的進一步處理。 ![](https://img.kancloud.cn/7a/92/7a92379febeeedcfad5c372b8a8b2508_1258x576.png) ## 如何處理滑動沖突 當父 View 和子 View 都有機會消費事件,但消費的時機不符合業務的需要(比如需要子 View 消費事件但父 View 先消費了),就會產生滑動沖突問題 解決辦法一般分為**內部攔截法**和**外部攔截法**。 **內部攔截法**就是通過重寫底層 View 的 dispatchTouchEvent 和 onTouchEvent 方法來決定是否消費事件。 **外部攔截法**就是重寫 ViewGroup 的 dispatchTouchEvent 和 onInterceptTouchEvent 方法決定是否把事件分發給 View。 兩種方法實際上就是對分發事件的 View 和被分發事件的 View 做不同的邏輯判斷。 ## 講講ACTION\_CANCEL事件? > 好的,看來平時沒少解決滑動沖突的問題哈。剛才你提到了事件序列對吧,你說說 ACTION\_CANCEL 事件是用來干嘛的? 前面我們提到在一個事件序列中,如果有 View 能夠消費事件,那么該事件序列所有的后續事件都會交給這個 View 處理。但如果不希望它處理全部的后續事件怎么辦?比如手指點擊一個 Button 然后滑出它的邊界。在這個事件序列中,我只希望 Button 處理它邊界內的 move 事件。對于邊界外的 move 事件,雖然它們都在一個事件序列中,但理論上不應該再傳遞給 Button 了。 ACTION\_CANCEL 就是用來解決這個問題的。當 Button 判斷 move 事件已經超出 view 的邊界時,會將自己的 mPrivateFlags 置為 cancel 狀態。等下次事件分發來臨,Button 的父 ViewGroup 會檢測 Button 的 mPrivateFlags,如果為 cancel 則將之前保存的 mFirstTouchTarget(也就是指向 Button 的引用) 設為 null,并向 Button 發送一個 ACTION\_CANCEL 事件,表示以后不會再接受事件了。 ![](https://img.kancloud.cn/96/50/9650037b3eff9754d20f30fea1f00950_1254x918.png) # 參考資料 [【面試官爸爸】嘮嘮Android事件分發?](https://juejin.cn/post/6984432840880422949) [必問的事件分發,你答得上來嗎](https://juejin.cn/post/6844903823824158733)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看