Android自定義View之事件分發機制總結
事件序列
(1)手指接觸屏幕後會產生一系列事件,事件分為3種:ACTION_DOWN(手指剛剛接觸螢幕)、ACTION_MOVE(手指在螢幕移動)、ACTION_UP(手指從螢幕鬆開)
(2)一個事件序列為ACTION_DOWN-->ACTION_MOVE-->...-->ACTION_UP
事件傳遞的順序
Activity-->Window-->decor view-->我們的layout,ViewGroup-->我們佈局中被點選的子View
如果我們的子View沒有處理事件,那事件就會反向向上傳遞回來:
我們佈局中被點選的子View-->上層的ViewGroup-->decor view-->Window-->Activity
如果所有的View都沒有消耗事件,那最後事件會傳回到Activity,由Activity處理(Activity的onTouchEvent()方法被呼叫)
三大方法
ViewGroup中有3個跟事件分發有關的方法,分別是 dispatchTouchEvent、 onInterceptTouchEvent、onTouchEvent。
(1)dispatchTouchEvent方法
dispatchTouchEvent方法用來進行事件的分發。事件傳遞到當前View時,這個方法就會被呼叫。dispatchTouchEvent方法裡面包含了具體的事件分發邏輯,返回結果受當前View的onTouchEvent方法和下級View的dispatchTouchEvent方法的影響。
(2)onInterceptTouchEvent方法
onInterceptTouchEvent方法在dispatchTouchEvent方法內部被呼叫,用來判斷是否攔截某個事件。如果當前View攔截了某個事件,那麼在同一個事件序列當中,此方法不會被再次呼叫,返回結果表示是否攔截當前事件。這個方法只有VewGroup中有,View中沒有。
(3)onTouchEvent方法
在dispatchTouchEvent方法中呼叫,用來處理點選事件,返回結果表示是否消耗當前事件,如果不消耗,則在同一個事件序列中,當前View無法再次接收到事件。
onTouchListener、onTouchEvent、onClickListener的優先順序
(1)onTouchListener和onTouchEvent都在dispatchTouchEvent方法中被呼叫,onClickListener在onTouchEvent方法中被呼叫
(2)onTouchListener的優先順序高於onTouchEvent方法,如果onTouchListener的onTouch方法返回true,則onTouchEvent方法不會被呼叫,當然onClickListener就更不會被呼叫了
(3)在onTouchEvent方法中,如果當前View設定了onClickListener,那麼onClickListener的onClick方法會被呼叫
(4)只要View的CLICKABLE和LONKG_CLICKABLE有一個為true,View就會消耗當前事件,也就是說onTouchEvent方法最後會返回true。
(5)View的LONG_CLICKABLE屬性預設為false,而CLICKABLE屬性和具體的View有關,可點選的View的CLICKABLE屬性為true,不可點選的View的CLICKABLE屬性為false。
ViewGroup中的事件分發邏輯
ViewGroup中的事件分發邏輯可以用一段虛擬碼來表述
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev)) {
consume = onTouchEvent();
}else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
從上述的虛擬碼中我們可以總結出ViewGroup中的事件分發流程:
(1)事件傳遞到ViewGroup時,dispatchTouchEvent方法會被呼叫。如果這個ViewGroup的onInterceptTouchEvent方法返回true,則表示它要攔截事件,事件就會交給當前ViewGroup的onTouchEvent方法處理。
(2)如果當前ViewGroup的onInterceptTouchEvent返回false,即不攔截事件,則會呼叫子元素的dispatchTouchEvent方法,這樣就把事件傳遞給了子元素。
(3)如果子元素沒有消耗事件,也就是子元素的dispatchTouchEvent方法返回false,那事件會由當前ViewGroup自己處理,當前ViewGroup的onTouchEvent會被呼叫。如果當前ViewGroup的dispatchTouchEvent方法也返回false,最後就會一層層往上,如果事件一直沒有被消耗,那麼最後Activity的onTouchEvent方法會被呼叫
(4)這裡需要理解一下的是ViewGroup繼承自View,ViewGroup中並沒有onTouchEvent方法。在所有子元素沒有消耗事件時,ViewGroup會呼叫父類,也就是View的dispatchTouchEvent方法,從而呼叫到onTouchEvent方法來自己處理事件,如果自己沒有消耗事件,dispatchTouchEvent方法就會返回false,從而將事件反向往上層傳遞。
(5)如果ACTION_DOWN事件子元素沒處理(onTouchEvent返回false),那這個事件序列的其他事件(MOVE和UP事件)都不會再分派給子元素處理。
(6)ViewGroup預設不攔截任何事件
(7)對於ACTION_DOWN事件,ViewGroup每次都會呼叫onInterceptTouchEvent方法來判斷是否需要攔截事件,一旦確定要攔截事件,後續的ACTION_MOVE和ACTION_UP事件都ViewGroup自己處理,不會傳遞給子View,也不會再呼叫onInterceptTouchEvent方法。所以onInterceptTouchEvent方法不是每次事件都會被呼叫的。
(8)子View可以通過requestDisallowInterceptTouchEvent方法來干預父元素的除了ACTION_DOWN意外的事件分發過程
View中的事件分發邏輯
requestDisallowInterceptTouchEvent方法
requestDisallowInterceptTouchEvent方法用於影響父元素的事件攔截策略,requestDisallowInterceptTouchEvent(true),表示不允許父元素攔截事件,這樣事件就會傳遞給子View。一般這個方法子View用的多,可以用來處理滑動衝突問題。
事件分發邏輯
(1)View中沒有onInterceptTouchEvent方法,所以一旦事件傳遞到View,那麼View的dispatchTouchEvent方法就會被呼叫。
(2)dispatchTouchEvent方法中處理事件的邏輯順序是onTouchListener–>onTouchEvent–>onClickListener。
(3)也就是說如果View設定了onTouchListener,那onTouchListener的onTouch方法會被呼叫,如果onTouch方法返回true,那事件就被消耗了,事件分發結束,onTouchEvent不會被呼叫。
(4)如果onTouch方法返回false,那麼onTouchEvent就會被呼叫。如果View設定了onClickListener,當ACTION_UP事件到來時,onTouchEvent中的onClickListener的onClick方法也會被呼叫。
(5)View一般都會消耗事件,如果View沒有消耗ACTION_DOWN事件,那後面ACTION_MOVE和ACTION_UP就都不會傳遞給View。
常用的滑動衝突處理邏輯
(1)利用父佈局的onInterceptTouchEvent方法
這個思路就是在父佈局需要處理事件時攔截下來,其他時候不攔截。有幾個注意點:
對於ACTION_DOWN事件,onInterceptTouchEvent方法必須返回false,因為一旦返回true,子元素永遠也接收不到事件了,那還解決個毛線衝突。
主要的邏輯就在ACTION_MOVE的處理上,需不需要攔截的邏輯在這裡根據需要來實現
對於ACTION_UP事件返回false,因為一旦父元素返回true,那子View就接受不到ACTION_UP事件了,也就無法觸發onClick事件。
(2)利用子View的requestDisallowInterceptTouchEvent方法
這個思路就是父佈局預設攔截除了ACTION_DOWN的所有事件,子View中在dispatchTouchEvent方法中根據需要來干預父佈局的攔截策略。預設不允許父佈局攔截事件,在需要父佈局處理事件時,通過requestDisallowInterceptTouchEvent(false)方法讓父佈局處理事件,其他時候都由子View處理。
注意點:
同樣的對於ACTION_DOWN事件,onInterceptTouchEvent方法必須返回false,其他事件預設返回true
在子View的dispatchTouchEvent方法中,對於ACTION_DOWN事件,通過呼叫requestDisallowInterceptTouchEvent(true)預設不允許父佈局攔截事件,這樣後續事件都交給子View處理
在子View的dispatchTouchEvent方法中,對於ACTION_MOVE事件,預設是子View處理,在需要父佈局處理時,呼叫requestDisallowInterceptTouchEvent(false)方法來讓父佈局攔截事件,交給父佈局處理。
相關推薦
Android自定義View之事件分發機制總結
事件序列 (1)手指接觸屏幕後會產生一系列事件,事件分為3種:ACTION_DOWN(手指剛剛接觸螢幕)、ACTION_MOVE(手指在螢幕移動)、ACTION_UP(手指從螢幕鬆開) (2)一個事件序列為ACTION_DOWN-->ACTION_MOV
Android自定義View之分貝儀
一、說明 最近在整理自定義View方面的知識,偶爾看到meizu工具箱的分貝儀效果,感覺它的實效效果還挺好的,遂想自己模擬實現練練手,廢話不多說,直接開擼。 二、效果圖 首先看一下效果圖: 看效果還挺炫酷
Android自定義View之Canvas
https://www.jianshu.com/p/fb18c28d6627 用繼承View的方式來自定義View,我們就需要重寫onDraw方法,也就是得咱自己來畫圖了。畫圖就得用到畫筆和畫布,也就是Paint和Canvas。我們來了解下Canvas。 Canvas Canvas我們可
Android 自定義View之Canvas詳解
自定義View的相關文章: Android 實現一個簡單的自定義View Android 自定義View步驟 Android Paint詳解 Android 自定義View之Canvas相關方法說明 Android 自定義View例項之 “京東跑”
Android : 自定義View之流式佈局
寫了一個很簡單的佈局 這是周圍圓框的drawable <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android">
android自定義View之仿通訊錄側邊欄滑動,實現A-Z字母檢索
我們的手機通訊錄一般都有這樣的效果,如下圖: OK,這種效果大家都見得多了,基本上所有的android手機通訊錄都有這樣的效果。那我們今天就來看看這個效果該怎麼實現。 一.概述 1.頁面功能分析 整體上來說,左邊是一個ListView,右邊是一個自定義View,但
Android自定義View之getTextBounds()
在Android自定義View的過程中一定會用到Paint,而paint屬性中有一個方法getTextBounds(String text,int start,int end,Rext bounds),它的中文解釋是:返回一個包含中文的矩形邊界,位置為(start,end) 英文解釋:Retur
Android自定義View之自定義屬性
在Android開發中經常會用到自定義View的情況,而在自定義View時,自定義屬性是必須用到的。 1、新建一個自定義View如CustomView 它的自定義屬性主要是通過declare-styleable標籤為其配置自定義屬性。具體做法是:在res/values/目錄下增加一個reso
Android -- 自定義view之StepView
先看看實現的效果: 2,首先我們來看看我們常規的自定義view的基礎步驟吧 1,繼承View,重寫構造方法 2,自定義屬性 3,重寫onMeasure()測量控制元件高度 4,重寫onDra
Android 自定義View 之 可隨意拖動的View
因為趕專案本人停更兩個月 從今天開始又可以更新了 今天說一下這個可隨意拖動的view 簡單說一下這個view效果 和 發展 一開始這種效果是使用在網頁端的特別是購物類 例如某寶 某東 購物車和客服視窗 都有使用這個懸浮可拖動的設計效果 後來才發展到的移動端 還有
Android 自定義View之咖啡動畫
文章目錄效果畫杯子畫杯墊畫煙霧 效果 大概思路 自定義view,直接繼承view 複寫onSizeChanged()方法,在此計算杯墊,杯子,煙霧效果的path 在onDraw()方法中,描繪杯墊,杯子 處理煙霧動畫效果 畫杯子 這裡需要畫兩部分內容,第
Android 自定義View之下雨動畫
文章目錄效果思路畫雲畫雨滴優化 效果 開始前先做個熱身( ˘•灬•˘ ) 自己實現比較容易,但是到了要出部落格整理思路,總結要點的時候就撓頭,不知雲所以,所以最簡單的還是 如果對安卓UI有興趣的朋友可以加我好友互相探討, 思路 思路比較簡單,整個view無
android自定義View之自定義EditText(新增刪除功能)
忙忙碌碌20天,新的專案終於接近尾聲了。今天公司召集幾個使用者體驗師和美工一起吐糟這20天做的這個新產品,對於產品提出了很多建議,這幾天就改介面了。在這個專案中大量的使用了EditText元件,並且添加了刪除功能。這裡面都是用RelativeLayou
android自定義view之畫圓隨著手指移動
public class MyView extends View { private Paint mFanPaint,mTextPaint;//扇形畫筆和文字畫筆 public float AxisX=100; public float AxisY=100; public MyView(
Android自定義view之實現帶checkbox的Snackbar
前言 最近專案要求實現一個類似於snackbar功能,但是又不完全是snackbar的外掛,本來想在Google提供的snackbar裡面進行更改,但是這樣太麻煩了,於是自己動手實現了一個snackbar。先看下效果圖: 1.要解決的問題 1.彈框裡面除了文字提示之外還有一個按鈕,這個按
android自定義View之3D索引效果
效果圖: 我的小霸王太卡了。 最近工作比較忙,今天搞了一下午才搞出來這個效果,這種效果有很多種實現方式,最常見的應該是用貝塞爾曲線實現的。今天我們來看另一種不同的實現方式,只需要用到 canvas.scale(),有沒有很好奇是怎麼實現的呢。 首先來說一下思路,只要有了思
android 自定義view之側滑效果
效果圖: 看網上的都是兩個view拼接,預設右側的不顯示,水平移動的時候把右側的view顯示出來。但是看最新版QQ上的效果不是這樣的,但給人的感覺卻很好,所以獻醜來一發比較高仿的。 知識點: 1、ViewDragHelper 的用法; 2、滑動衝突的解決; 3、自定
android 自定義view之選座功能
效果圖: 介面比較粗糙,主要看原理。 這個介面主要包括以下幾部分 1、座位 2、左邊的排數 3、左上方的縮圖 4、縮圖中的紅色區域 5、手指移動時跟隨移動 6、兩個手指縮放時跟隨縮放 主要技術點 1、矩陣Matrix 2、GestureDetector與S
Android自定義View之實現簡單炫酷的球體進度球
前言 最近一直在研究自定義view,正好專案中有一個根據下載進度來實現球體進度的需求,所以自己寫了個進度球,程式碼非常簡單。先看下效果: 效果還是非常不錯的。 準備知識 要實現上面的效果我們只要掌握兩個知識點就好了,一個是Handler機制,用於發訊息重新整理我們的進度球,一個是clip
從零開始學Android自定義View之動畫系列——屬性動畫(3)
屬性動畫對補間動畫進行了很大幅度的改進,之前補間動畫可以做到的屬性動畫也能做到,補間動畫做不到的現在屬性動畫也可以做到了。因此,今天我們就來學習一下屬性動畫的高階用法,看看如何實現一些補間動畫所無法實現的功能。 ValueAnimator的高階用法 補間