Android MotionEvent 詳解,之前用了兩篇文章 事件分發(fā)機(jī)制原理 和 事件分發(fā)機(jī)制詳解 來講解事件分發(fā)蕉鸳,而作為事件分發(fā)主角之一的 MotionEvent 并沒有過多的說明,本文就帶大家了解 MotionEvent 的相關(guān)內(nèi)容,簡要介紹觸摸事件崇败,主要包括 單點(diǎn)觸控远豺、多點(diǎn)觸控、鼠標(biāo)事件 以及 getAction() 和 getActionMasked() 的區(qū)別淮捆。
Android 將所有的輸入事件都放在了 MotionEvent 中郁油,隨著安卓的不斷發(fā)展壯大,MotionEvent 也開始變得越來越復(fù)雜攀痊,下面是我自己整理的 MotionEvent 大事記:
版本號(hào) | 更新內(nèi)容 |
---|---|
Android 1.0 (API 1 ) | 支持單點(diǎn)觸控和軌跡球的事件桐腌。 |
Android 1.6 (API 4 ) | 支持手勢。 |
Android 2.0 (API 5 ) | 支持多點(diǎn)觸控苟径。 |
Android 3.1 (API 12) | 支持觸控筆案站,鼠標(biāo),鍵盤棘街,操縱桿蟆盐,游戲控制器等輸入工具。 |
以上僅僅是簡要的說明幾次比較大的變動(dòng)遭殉,細(xì)小的修復(fù)和更新不計(jì)其數(shù)石挂,此處就不一一列出了,反正也沒人關(guān)心這些東西险污。
MotionEvent 負(fù)責(zé)集中處理所有類型設(shè)備的輸入事件誊稚,但是由于某些設(shè)備使用的幾率較小本文會(huì)忽略講解,或者簡要講解罗心,例如:
1里伯、軌跡球只出現(xiàn)在最早的設(shè)備上,現(xiàn)代的設(shè)備上已經(jīng)見不到了渤闷,本文不再敘述疾瓮。
2、觸控筆和手指處理流程基本相同飒箭,不再多說狼电。
3蜒灰、鼠標(biāo)在手機(jī)上使用概率也比較小,會(huì)在文末簡要介紹肩碟。
單點(diǎn)觸控
單點(diǎn)觸控就非常簡單啦强窖,入門的工程師都會(huì)用,上一篇文章也簡要介紹過削祈,主要涉及以下幾個(gè)事件:
事件 | 簡介 |
---|---|
ACTION_DOWN | 手指 初次接觸到屏幕 時(shí)觸發(fā)翅溺。 |
ACTION_MOVE | 手指 在屏幕上滑動(dòng) 時(shí)觸發(fā),會(huì)多次觸發(fā)髓抑。 |
ACTION_UP | 手指 離開屏幕 時(shí)觸發(fā)咙崎。 |
ACTION_CANCEL | 事件 被上層攔截 時(shí)觸發(fā)。 |
ACTION_OUTSIDE | 手指 不在控件區(qū)域 時(shí)觸發(fā)吨拍。 |
和以下的幾個(gè)方法:
方法 | 簡介 |
---|---|
getAction() | 獲取事件類型褪猛。 |
getX() | 獲得觸摸點(diǎn)在當(dāng)前 View 的 X 軸坐標(biāo)。 |
getY() | 獲得觸摸點(diǎn)在當(dāng)前 View 的 Y 軸坐標(biāo)羹饰。 |
getRawX() | 獲得觸摸點(diǎn)在整個(gè)屏幕的 X 軸坐標(biāo)伊滋。 |
getRawY() | 獲得觸摸點(diǎn)在整個(gè)屏幕的 Y 軸坐標(biāo)。 |
關(guān)于
get
和getRaw
的區(qū)別可以參考這一篇文章 安卓自定義View基礎(chǔ)-坐標(biāo)系
單點(diǎn)觸控一次簡單的交互流程是這樣的:
手指落下(ACTION_DOWN) -> 多次移動(dòng)(ACTION_MOVE) -> 離開(ACTION_UP)
- 本次事例中 ACTION_MOVE 有多次觸發(fā)队秩。
- 如果僅僅是單擊(手指按下再抬起)新啼,不會(huì)觸發(fā) ACTION_MOVE。
針對單點(diǎn)觸控的事件處理一般是這樣寫的:
@Override
public boolean onTouchEvent(MotionEvent event) {
// ▼ 注意這里使用的是 getAction()刹碾,先埋一個(gè)小尾巴燥撞。
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
// 手指按下
break;
case MotionEvent.ACTION_MOVE:
// 手指移動(dòng)
break;
case MotionEvent.ACTION_UP:
// 手指抬起
break;
case MotionEvent.ACTION_CANCEL:
// 事件被攔截
break;
case MotionEvent.ACTION_OUTSIDE:
// 超出區(qū)域
break;
}
return super.onTouchEvent(event);
}
相信小伙伴對此已經(jīng)非常熟悉了,經(jīng)常使用的東西迷帜,我也不啰嗦了物舒。
但其中有兩個(gè)比較特殊的事件: ACTION_CANCEL
和 ACTION_OUTSIDE
。
為什么說特殊呢戏锹,因?yàn)樗鼈兪怯沙绦蛴|發(fā)而產(chǎn)生的冠胯,而且觸發(fā)條件也非常特殊,通常情況下即便不處理這兩個(gè)事件也沒有什么問題锦针。接下來我們就扒一扒它們的真面目:
ACTION_CANCEL
ACTION_CANCEL
的觸發(fā)條件是事件被上層攔截荠察,然而我們在 事件分發(fā)機(jī)制原理 一文中了解到當(dāng)事件被上層 View 攔截的時(shí)候,ChildView 是收不到任何事件的奈搜,ChildView 收不到任何事件悉盆,自然也不會(huì)收到 ACTION_CANCEL
了,所以說這個(gè) ACTION_CANCEL
的正確觸發(fā)條件并不是這樣馋吗,那么是什么呢焕盟?
事實(shí)上,只有上層 View 回收事件處理權(quán)的時(shí)候宏粤,ChildView 才會(huì)收到一個(gè) ACTION_CANCEL
事件脚翘。
這樣說可能不太容易理解灼卢,咱舉個(gè)例子?
例如:上層 View 是一個(gè) RecyclerView,它收到了一個(gè)
ACTION_DOWN
事件来农,由于這個(gè)可能是點(diǎn)擊事件鞋真,所以它先傳遞給對應(yīng) ItemView,詢問 ItemView 是否需要這個(gè)事件沃于,然而接下來又傳遞過來了一個(gè)ACTION_MOVE
事件涩咖,且移動(dòng)的方向和 RecyclerView 的可滑動(dòng)方向一致,所以 RecyclerView 判斷這個(gè)事件是滾動(dòng)事件揽涮,于是要收回事件處理權(quán),這時(shí)候?qū)?yīng)的 ItemView 會(huì)收到一個(gè)ACTION_CANCEL
饿肺,并且不會(huì)再收到后續(xù)事件蒋困。通俗一點(diǎn)?
RecyclerView:兒砸敬辣,這里有一個(gè)
ACTION_DOWN
你看你要不要雪标。
ItemView :好嘞,我看看溉跃。
RecyclerView:噫村刨?居然是移動(dòng)事件ACTION_MOVE
,我要滾起來了撰茎,兒砸嵌牺,我可能要把你送去你姑父家(緩存區(qū))了,在這之前給你一個(gè)ACTION_CANCEL
龄糊,你要收好啊逆粹。
ItemView :……這是實(shí)際開發(fā)中最有可能見到
ACTION_CANCEL
的場景了。
ACTION_OUTSIDE
ACTION_OUTSIDE
的觸發(fā)條件更加奇葩炫惩,從字面上看僻弹,outside 意思不就是超出區(qū)域么?然而不論你如何滑動(dòng)超出控件區(qū)域都不會(huì)觸發(fā) ACTION_OUTSIDE
這個(gè)事件他嚷。相信很多魔法師都對此很是疑惑蹋绽,說好的超出區(qū)域呢?
實(shí)際上這個(gè)事件根本就不是在這里用的筋蓖,看官方解釋(裝一下逼):
A movement has happened outside of the normal bounds of the UI element. This does not provide a full gesture, but only the initial location of the movement/touch.
一個(gè)觸摸事件已經(jīng)發(fā)生了UI元素的正常范圍之外卸耘。因此不再提供完整的手勢,只提供 運(yùn)動(dòng)/觸摸 的初始位置粘咖。
我們知道鹊奖,正常情況下,如果初始點(diǎn)擊位置在該視圖區(qū)域之外涂炎,該視圖根本不可能會(huì)收到事件忠聚,然而设哗,萬事萬物都不是絕對的,肯定還有一些特殊情況两蟀,你可曾還記得點(diǎn)擊 Dialog 區(qū)域外關(guān)閉嗎网梢?Dialog 就是一個(gè)特殊的視圖(沒有占滿屏幕大小的窗口),能夠接收到視圖區(qū)域外的事件(雖然在通常情況下你根本用不到這個(gè)事件)赂毯,除了 Dialog 之外战虏,你最可能看到這個(gè)事件的場景是懸浮窗,當(dāng)然啦党涕,想要接收到視圖之外的事件需要一些特殊的設(shè)置烦感。
設(shè)置視圖的 WindowManager 布局參數(shù)的 flags為
FLAG_WATCH_OUTSIDE_TOUCH
,這樣點(diǎn)擊事件發(fā)生在這個(gè)視圖之外時(shí)膛堤,該視圖就可以接收到一個(gè)ACTION_OUTSIDE
事件手趣。參見StackOverflow:How to dismiss the dialog with click on outside of the dialog?
由于這個(gè)事件用到的幾率比較小,此處就不展開敘述了肥荔,以后用到的時(shí)候再詳細(xì)講解绿渣。
多點(diǎn)觸控
Android 在 2.0 版本的時(shí)候開始支持多點(diǎn)觸控,一旦出現(xiàn)了多點(diǎn)觸控燕耿,很多東西就突然之間變得麻煩起來了中符,首先要解決的問題就是 多個(gè)手指同時(shí)按在屏幕上,會(huì)產(chǎn)生很多的事件誉帅,這些事件該如何區(qū)分呢淀散?
為了區(qū)分這些事件,工程師們用了一個(gè)很簡單的辦法--編號(hào)蚜锨,當(dāng)手指第一次按下時(shí)產(chǎn)生一個(gè)唯一的號(hào)碼吧凉,手指抬起或者事件被攔截就回收編號(hào),就這么簡單踏志。
第一次按下的手指特殊處理作為主指針阀捅,之后按下的手指作為輔助指針,然后隨之衍生出來了以下事件(注意增加的事件和事件簡介的變化):
事件 | 簡介 |
---|---|
ACTION_DOWN | 第一個(gè) 手指 初次接觸到屏幕 時(shí)觸發(fā)针余。 |
ACTION_MOVE | 手指 在屏幕上滑動(dòng) 時(shí)觸發(fā)饲鄙,會(huì)多次觸發(fā)。 |
ACTION_UP | 最后一個(gè) 手指 離開屏幕 時(shí)觸發(fā)圆雁。 |
ACTION_POINTER_DOWN | 有非主要的手指按下(即按下之前已經(jīng)有手指在屏幕上)忍级。 |
ACTION_POINTER_UP | 有非主要的手指抬起(即抬起之后仍然有手指在屏幕上)。 |
以下事件類型不推薦使用 | ------------------ |
第 2 個(gè)手指按下伪朽,已廢棄轴咱,不推薦使用。 | |
第 3 個(gè)手指按下,已廢棄朴肺,不推薦使用窖剑。 | |
第 4 個(gè)手指按下,已廢棄戈稿,不推薦使用西土。 | |
第 2 個(gè)手指抬起,已廢棄鞍盗,不推薦使用需了。 | |
第 3 個(gè)手指抬起,已廢棄般甲,不推薦使用肋乍。 | |
第 4 個(gè)手指抬起,已廢棄敷存,不推薦使用墓造。 |
和以下方法:
方法 | 簡介 |
---|---|
getActionMasked() | 與 getAction() 類似,多點(diǎn)觸控必須使用這個(gè)方法獲取事件類型历帚。 |
getActionIndex() | 獲取該事件是哪個(gè)指針(手指)產(chǎn)生的滔岳。 |
getPointerCount() | 獲取在屏幕上手指的個(gè)數(shù)杠娱。 |
getPointerId(int pointerIndex) | 獲取一個(gè)指針(手指)的唯一標(biāo)識(shí)符ID挽牢,在手指按下和抬起之間ID始終不變。 |
findPointerIndex(int pointerId) | 通過PointerId獲取到當(dāng)前狀態(tài)下PointIndex摊求,之后通過PointIndex獲取其他內(nèi)容禽拔。 |
getX(int pointerIndex) | 獲取某一個(gè)指針(手指)的X坐標(biāo) |
getY(int pointerIndex) | 獲取某一個(gè)指針(手指)的Y坐標(biāo) |
由于多點(diǎn)觸控部分涉及內(nèi)容比較多,也很復(fù)雜室叉,我準(zhǔn)備單獨(dú)用一篇文章進(jìn)行詳細(xì)敘述睹栖,所以這里只敘述一些基礎(chǔ)的內(nèi)容作為鋪墊:
getAction() 與 getActionMasked()
當(dāng)多個(gè)手指在屏幕上按下的時(shí)候,會(huì)產(chǎn)生大量的事件茧痕,如何在獲取事件類型的同時(shí)區(qū)分這些事件就是一個(gè)大問題了野来。
一般來說我們可以通過為事件添加一個(gè)int類型的index屬性來區(qū)分,但是我們知道谷歌工程師是有潔癖的(在 自定義View分類與流程 的onMeasure中已經(jīng)見識(shí)過了)踪旷,為了添加一個(gè)通常數(shù)值不會(huì)超過10的index屬性就浪費(fèi)一個(gè)int大小的空間簡直是不能忍受的曼氛,于是工程師們將這個(gè)index屬性和事件類型直接合并了。
int類型共32位(0x00000000)令野,他們用最低8位(0x000000ff)表示事件類型舀患,再往前的8位(0x0000ff00)表示事件編號(hào),以手指按下為例講解數(shù)值是如何合成的:
ACTION_DOWN 的默認(rèn)數(shù)值為 (0x00000000)
ACTION_POINTER_DOWN 的默認(rèn)數(shù)值為 (0x00000005)
手指按下 | 觸發(fā)事件(數(shù)值) |
---|---|
第1個(gè)手指按下 | ACTION_DOWN (0x00000000) |
第2個(gè)手指按下 | ACTION_POINTER_DOWN (0x00000105) |
第3個(gè)手指按下 | ACTION_POINTER_DOWN (0x00000205) |
第4個(gè)手指按下 | ACTION_POINTER_DOWN (0x00000305) |
注意:
上面表格中用粗體標(biāo)示出的數(shù)值气破,可以看到隨著按下手指數(shù)量的增加聊浅,這個(gè)數(shù)值也是一直變化的,進(jìn)而導(dǎo)致我們使用 getAction()
獲取到的數(shù)值無法與標(biāo)準(zhǔn)的事件類型進(jìn)行對比,為了解決這個(gè)問題低匙,他們創(chuàng)建了一個(gè) getActionMasked()
方法旷痕,這個(gè)方法可以清除index數(shù)值,讓其變成一個(gè)標(biāo)準(zhǔn)的事件類型努咐。
1苦蒿、多點(diǎn)觸控時(shí)必須使用 getActionMasked()
來獲取事件類型。
2渗稍、單點(diǎn)觸控時(shí)由于事件數(shù)值不變佩迟,使用 getAction()
和 getActionMasked()
兩個(gè)方法都可以。
3竿屹、使用 getActionIndex() 可以獲取到這個(gè)index數(shù)值报强。不過請注意,getActionIndex() 只在 down 和 up 時(shí)有效拱燃,move 時(shí)是無效的秉溉。
目前來說獲取事件類型使用 getActionMasked()
就行了,但是如果一定要編譯時(shí)兼容古董版本的話碗誉,可以考慮使用這樣的寫法:
final int action = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO)
? event.getActionMasked()
: event.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
// TODO
break;
}
PointId
雖然前面剛剛說了一個(gè) actionIndex召嘶,可以使用 getActionIndex() 獲得,但通過 actionIndex 字面意思知道哮缺,這個(gè)只表示事件的序號(hào)弄跌,而且根據(jù)其說明文檔解釋,這個(gè) ActionIndex 只有在手指按下(down)和抬起(up)時(shí)是有用的尝苇,在移動(dòng)(move)時(shí)是沒有用的铛只,事件追蹤非常重要的一環(huán)就是移動(dòng)(move),然而它卻沒卵用糠溜,這也太不實(shí)在了 ( ̄Д ̄)?
鄭重聲明:追蹤事件流淳玩,請認(rèn)準(zhǔn) PointId,這是唯一官方指定標(biāo)準(zhǔn)非竿,不要相信 ActionIndex 那個(gè)小婊砸蜕着。
PointId 在手指按下時(shí)產(chǎn)生,手指抬起或者事件被取消后消失红柱,是一個(gè)事件流程中唯一不變的標(biāo)識(shí)承匣,可以在手指按下時(shí) 通過 getPointerId(int pointerIndex)
獲得。 (參數(shù)中的 pointerIndex 就是 actionIndex)
關(guān)于事件流的追蹤等問題在講解多點(diǎn)觸控時(shí)再詳細(xì)講解豹芯。
歷史數(shù)據(jù)(批處理)
由于我們的設(shè)備非常靈敏悄雅,手指稍微移動(dòng)一下就會(huì)產(chǎn)生一個(gè)移動(dòng)事件,所以移動(dòng)事件會(huì)產(chǎn)生的特別頻繁铁蹈,為了提高效率宽闲,系統(tǒng)會(huì)將近期的多個(gè)移動(dòng)事件(move)按照事件發(fā)生的順序進(jìn)行排序打包放在同一個(gè) MotionEvent 中众眨,與之對應(yīng)的產(chǎn)生了以下方法:
事件 | 簡介 |
---|---|
getHistorySize() | 獲取歷史事件集合大小 |
getHistoricalX(int pos) | 獲取第pos個(gè)歷史事件x坐標(biāo) |
(pos < getHistorySize()) | |
getHistoricalY(int pos) | 獲取第pos個(gè)歷史事件y坐標(biāo) |
(pos < getHistorySize()) | |
getHistoricalX (int pin, int pos) | 獲取第pin個(gè)手指的第pos個(gè)歷史事件x坐標(biāo) |
(pin < getPointerCount(), pos < getHistorySize() ) | |
getHistoricalY (int pin, int pos) | 獲取第pin個(gè)手指的第pos個(gè)歷史事件y坐標(biāo) |
(pin < getPointerCount(), pos < getHistorySize() ) |
注意:
- pin 全稱是 pointerIndex,表示第幾個(gè)手指容诬,此處為了節(jié)省空間使用了縮寫娩梨。
- 歷史數(shù)據(jù)只有 ACTION_MOVE 事件。
- 歷史數(shù)據(jù)單點(diǎn)觸控和多點(diǎn)觸控均可以用览徒。
下面是官方文檔給出的一個(gè)簡單使用示例:
void printSamples(MotionEvent ev) {
final int historySize = ev.getHistorySize();
final int pointerCount = ev.getPointerCount();
for (int h = 0; h < historySize; h++) {
System.out.printf("At time %d:", ev.getHistoricalEventTime(h));
for (int p = 0; p < pointerCount; p++) {
System.out.printf(" pointer %d: (%f,%f)",
ev.getPointerId(p), ev.getHistoricalX(p, h), ev.getHistoricalY(p, h));
}
}
System.out.printf("At time %d:", ev.getEventTime());
for (int p = 0; p < pointerCount; p++) {
System.out.printf(" pointer %d: (%f,%f)",
ev.getPointerId(p), ev.getX(p), ev.getY(p));
}
}
獲取事件發(fā)生的時(shí)間
獲取事件發(fā)生的時(shí)間狈定。
方法 | 簡介 |
---|---|
getDownTime() | 獲取手指按下時(shí)的時(shí)間。 |
getEventTime() | 獲取當(dāng)前事件發(fā)生的時(shí)間习蓬。 |
getHistoricalEventTime(int pos) | 獲取歷史事件發(fā)生的時(shí)間纽什。 |
- pos 表示歷史數(shù)據(jù)中的第幾個(gè)數(shù)據(jù)。( pos < getHistorySize() )
- 返回值類型為 long躲叼,單位是毫秒芦缰。
獲取壓力(接觸面積大小)
MotionEvent支持獲取某些輸入設(shè)備(手指或觸控筆)的與屏幕的接觸面積和壓力大小,主要有以下方法:
描述中使用了手指枫慷,觸控筆也是一樣的让蕾。
方法 | 簡介 |
---|---|
getSize () | 獲取第1個(gè)手指與屏幕接觸面積的大小 |
getSize (int pin) | 獲取第pin個(gè)手指與屏幕接觸面積的大小 |
getHistoricalSize (int pos) | 獲取歷史數(shù)據(jù)中第1個(gè)手指在第pos次事件中的接觸面積 |
getHistoricalSize (int pin, int pos) | 獲取歷史數(shù)據(jù)中第pin個(gè)手指在第pos次事件中的接觸面積 |
getPressure () | 獲取第一個(gè)手指的壓力大小 |
getPressure (int pin) | 獲取第pin個(gè)手指的壓力大小 |
getHistoricalPressure (int pos) | 獲取歷史數(shù)據(jù)中第1個(gè)手指在第pos次事件中的壓力大小 |
getHistoricalPressure (int pin, int pos) | 獲取歷史數(shù)據(jù)中第pin個(gè)手指在第pos次事件中的壓力大小 |
- pin 全稱是 pointerIndex,表示第幾個(gè)手指或听。(pin < getPointerCount() )
- pos 表示歷史數(shù)據(jù)中的第幾個(gè)數(shù)據(jù)探孝。( pos < getHistorySize() )
注意:
1、獲取接觸面積大小和獲取壓力大小是需要硬件支持的誉裆。
2顿颅、非常不幸的是大部分設(shè)備所使用的電容屏不支持壓力檢測,但能夠大致檢測出接觸面積找御。
3元镀、大部分設(shè)備的 getPressure()
是使用接觸面積來模擬的绍填。
4霎桅、由于某些未知的原因(可能系統(tǒng)版本和硬件問題),某些設(shè)備不支持該方法讨永。
我用不同的設(shè)備對這兩個(gè)方法進(jìn)行了測試滔驶,然而不同設(shè)備測試出來的結(jié)果不相同,之后經(jīng)過我多方查證卿闹,發(fā)現(xiàn)是系統(tǒng)問題揭糕,有的設(shè)備上只有 getSize()
能用,有的設(shè)備上只有 getPressure()
能用锻霎,而有的則兩個(gè)都不能用著角。
由于獲取接觸面積和獲取壓力大小受系統(tǒng)和硬件影響,使用的時(shí)候一定要進(jìn)行數(shù)據(jù)檢測旋恼,以防因?yàn)樵O(shè)備問題而導(dǎo)致程序出錯(cuò)吏口。
鼠標(biāo)事件
由于觸控筆事件和手指事件處理流程大致相同,所以就不講解了,這里講解一下與鼠標(biāo)相關(guān)的幾個(gè)事件:
事件 | 簡介 |
---|---|
ACTION_HOVER_ENTER | 指針移入到窗口或者View區(qū)域产徊,但沒有按下昂勒。 |
ACTION_HOVER_MOVE | 指針在窗口或者View區(qū)域移動(dòng),但沒有按下舟铜。 |
ACTION_HOVER_EXIT | 指針移出到窗口或者View區(qū)域戈盈,但沒有按下。 |
ACTION_SCROLL | 滾輪滾動(dòng)谆刨,可以觸發(fā)水平滾動(dòng)(AXIS_HSCROLL)或者垂直滾動(dòng)(AXIS_VSCROLL) |
注意:
1塘娶、這些事件類型是 安卓4.0 (API 14) 才添加的。
2痊夭、使用 getActionMasked()
獲得這些事件類型血柳。
3、這些事件不會(huì)傳遞到 onTouchEvent(MotionEvent) 而是傳遞到 onGenericMotionEvent(MotionEvent) 生兆。
輸入設(shè)備類型判斷
輸入設(shè)備類型判斷也是安卓4.0 (API 14) 才添加的难捌,主要包括以下幾種設(shè)備:
設(shè)備類型 | 簡介 |
---|---|
TOOL_TYPE_ERASER | 橡皮擦 |
TOOL_TYPE_FINGER | 手指 |
TOOL_TYPE_MOUSE | 鼠標(biāo) |
TOOL_TYPE_STYLUS | 手寫筆 |
TOOL_TYPE_UNKNOWN | 未知類型 |
使用 getToolType(int pointerIndex)
來獲取對應(yīng)的輸入設(shè)備類型,pointIndex可以為0鸦难,但必須小于 getPointerCount()
根吁。
文章搬運(yùn)自 安卓自定義View進(jìn)階-MotionEvent詳解