Android 和 ChromeOS 提供多種 API,可協助您建構提供
絕佳的觸控筆體驗
MotionEvent
類別會公開
觸控筆與螢幕互動的相關資訊,包括觸控筆壓力
方向、傾斜度、懸停和手掌偵測。低延遲圖像和動態
預測程式庫能強化觸控筆在螢幕上算繪的功能
就像使用紙筆一樣自然
MotionEvent
MotionEvent
類別代表使用者輸入內容的互動情形,例如位置
以及畫面上的觸控指標移動如為觸控筆輸入,請MotionEvent
也會提供壓力、方向、傾斜度和懸停資料。
事件資料
如要存取 MotionEvent
資料,請在元件中新增 pointerInput
修飾符:
@Composable
fun Greeting() {
Text(
text = "Hello, Android!", textAlign = TextAlign.Center, style = TextStyle(fontSize = 5.em),
modifier = Modifier
.pointerInput(Unit) {
awaitEachGesture {
while (true) {
val event = awaitPointerEvent()
event.changes.forEach { println(it) }
}
}
},
)
}
MotionEvent
物件提供與 UI 以下方面相關的資料
事件:
- 動作:對裝置進行的實體互動操作,例如輕觸螢幕、 在螢幕表面上移動指標,將指標懸停在畫面上 途徑
- 指標:與螢幕互動的物件 ID,包括手指、 觸控筆, 滑鼠
- 軸:資料類型,包括 x 和 y 座標、壓力、傾斜度、方向、 懸停 (距離)
動作
如要實作觸控筆支援功能,您需要瞭解使用者的動作 成效。
MotionEvent
提供多種 ACTION
常數,可用於定義動作
事件。觸控筆最重要的動作包括:
動作 | 說明 |
---|---|
ACTION_DOWN ACTION_POINTER_DOWN |
指標已與螢幕接觸。 |
ACTION_MOVE | 指標在畫面上移動。 |
ACTION_UP ACTION_POINTER_UP |
指標不再與螢幕接觸 |
ACTION_CANCEL | 之前或當下的動作集應取消的時機。 |
您的應用程式可以在 ACTION_DOWN
時執行新的筆觸等工作
觸發時,請使用 ACTION_MOVE,
繪製筆觸,在出現時則完成筆觸
ACTION_UP
已觸發。
針對特定指定,從 ACTION_DOWN
到 ACTION_UP
的一組 MotionEvent
動作
指標稱為動作集
指標
大部分的螢幕為多點觸控功能:系統會為手指指派指標, 觸控筆、滑鼠或其他指標物件與螢幕互動。指標 索引可讓您取得特定指標的軸資訊,例如 第一隻手指觸碰螢幕或第二隻手指的位置。
指標索引範圍從 0 到傳回的指標數。
MotionEvent#pointerCount()
敬上
減號 1。
指標的軸值可使用 getAxisValue(axis,
pointerIndex)
方法存取。
省略指標索引時,系統會傳回第一個
指標,指標 0。
MotionEvent
物件包含使用中指標類型的相關資訊。個人中心
可以藉由疊代指標索引並呼叫
這個
getToolType(pointerIndex)
敬上
方法。
如要進一步瞭解指標,請參閱「處理多點觸控 手勢。
觸控筆輸入
你可以使用下列條件篩選觸控筆輸入內容
TOOL_TYPE_STYLUS
:
val isStylus = TOOL_TYPE_STYLUS == event.getToolType(pointerIndex)
您也可以使用觸控筆做為橡皮擦,
TOOL_TYPE_ERASER
:
val isEraser = TOOL_TYPE_ERASER == event.getToolType(pointerIndex)
觸控筆的軸資料
ACTION_DOWN
和 ACTION_MOVE
提供觸控筆的軸資料,包括 x 和
y 座標、壓力、方向、傾斜度和懸停距離。
如要啟用這類資料的存取權,MotionEvent
API 提供了
getAxisValue(int)
,
參數是下列任一軸的 ID:
Axis | getAxisValue() 的傳回值 |
---|---|
AXIS_X |
動作事件的 X 座標。 |
AXIS_Y |
動作事件的 Y 座標。 |
AXIS_PRESSURE |
如果是觸控螢幕或觸控板,則為以手指、觸控筆或其他指標施加的壓力。如果是滑鼠或軌跡球,按下主按鈕的值為 1,否則為 0。 |
AXIS_ORIENTATION |
如果是觸控螢幕或觸控板,則為手指、觸控筆或其他指標相對於裝置垂直面的方向。 |
AXIS_TILT |
觸控筆的傾斜度,以弧度為單位。 |
AXIS_DISTANCE |
觸控筆與螢幕間的距離。 |
舉例來說,MotionEvent.getAxisValue(AXIS_X)
會傳回
這是第一個指標
另請參閱「處理多點觸控 手勢。
位置
您可以使用下列呼叫擷取指標的 x 和 y 座標:
MotionEvent#getAxisValue(AXIS_X)
或MotionEvent#getX()
MotionEvent#getAxisValue(AXIS_Y)
或MotionEvent#getY()
氣壓
您可以使用
MotionEvent#getAxisValue(AXIS_PRESSURE)
或針對第一個指標
MotionEvent#getPressure()
。
觸控螢幕或觸控板的壓力值介於 0 (否) 壓力) 和 1 之間,但可能會傳回更高的值 (視螢幕而定) 校正。
方向
方向是指觸控筆指向的方向。
您可以使用 getAxisValue(AXIS_ORIENTATION)
或
getOrientation()
(用於第一個指標)。
針對觸控筆,系統會以弧度值傳回 0 到 pi (π) 的弧度值 順時針或逆時針方向,或 0 到 -pi。
您可以透過方向實作逼真的筆刷體驗。舉例來說, 觸控筆代表平坦筆刷,筆刷的寬度則取決於 觸控筆方向
垂直運鏡
傾斜度指的是觸控筆相對於螢幕的傾斜度。
傾斜度會傳回觸控筆的正角 (以弧度為單位),其中 0 是 垂直方向與 π/2 垂直。
您可以使用 getAxisValue(AXIS_TILT)
擷取傾斜度 (
第一個指標)。
傾斜度可用來盡可能重現真實工具,例如 使用傾斜的鉛筆來模擬陰影。
懸停
可透過以下方式取得觸控筆與螢幕間的距離:
getAxisValue(AXIS_DISTANCE)
。此方法會傳回 0.0 (與
螢幕),並在觸控筆離開螢幕時調出更高的值。懸停
螢幕和觸控筆的六邊形 (point) 之間的距離取決於
同時具備螢幕和觸控筆的製造商資訊由於導入方式可能
不同,請勿仰賴精確的值來提供應用程式的重要功能。
觸控筆懸停功能可用來預覽筆刷大小,或表示 按鈕。
注意:Compose 提供的修飾符會影響 UI 元素的互動狀態:
hoverable
:將元件設為可透過指標的進入/離開事件,成為可懸停的元件。indication
:在發生互動時為此元件繪製視覺效果。
防止誤觸、導覽及不必要的輸入
多點觸控螢幕有時可能會註冊不必要的觸控動作,例如
使用者在手寫時自然會把手放在螢幕上,以便尋求支援。
防止誤觸機制可偵測這種行為,並通知您
最後一個 MotionEvent
集應取消
因此,您必須保留使用者輸入內容的記錄,以免不必要的觸控行為 就可以從畫面上移除正確的使用者輸入內容 重新轉譯。
ACTION_CANCEL 和 FLAG_CANCELED
ACTION_CANCEL
和
FLAG_CANCELED
是
這兩種 ID 都會用來通知您,先前的 MotionEvent
組合應該
取消了上一個 ACTION_DOWN
,因此您可以復原這項操作
特定指標的繪圖應用程式的筆觸。
ACTION_CANCEL
已在 Android 1.0 中新增 (API 級別 1)
ACTION_CANCEL
表示應取消上一組動作事件。
如果偵測到以下任一項目,就會觸發 ACTION_CANCEL
:
- 導覽手勢
- 防止誤觸
觸發 ACTION_CANCEL
時,建議您使用
getPointerId(getActionIndex())
。接著,從輸入記錄中移除使用該指標建立的筆觸,然後重新轉譯場景。
FLAG_CANCELED
已在 Android 13 (API 級別 33) 中新增
FLAG_CANCELED
敬上
表示指標上移是使用者無意輕觸。旗標是
通常是在使用者不小心觸碰螢幕時設定,例如按住
或將手掌放在螢幕上。
您可以透過下列方式存取標記值:
val cancel = (event.flags and FLAG_CANCELED) == FLAG_CANCELED
如果已設定此標記,您需要從最後 MotionEvent
的最後一個集上,
從這個指標選取 ACTION_DOWN
。
和 ACTION_CANCEL
一樣,您可以透過 getPointerId(actionIndex)
找到指標。
全螢幕、無邊框設計和導覽手勢
如果應用程式為全螢幕模式且在邊緣附近設有可操作元素 (例如 現在可以從繪圖或記事應用程式的畫布 顯示導覽畫面或將應用程式移至背景都可能造成 在這幅畫布上做出不必要的觸控動作
如要防止手勢在應用程式中觸發不必要的觸控動作,您可以採取下列做法:
利用插邊和
ACTION_CANCEL
。
另請參閱「防止誤觸、導覽及不必要的輸入」。 專區。
使用
setSystemBarsBehavior()
敬上
方法和
BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
。
/
WindowInsetsController
如何避免導覽手勢造成不必要的觸控事件:
// Configure the behavior of the hidden system bars.
windowInsetsController.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
如要進一步瞭解插邊和手勢管理,請參閱:
低延遲
延遲時間是指硬體、系統和應用程式所需的處理時間 以及轉譯使用者輸入內容
延遲時間 = 硬體和 OS 的輸入處理 + 應用程式處理 + 系統組合
- 硬體轉譯
延遲的來源
- 正在透過觸控螢幕 (硬體) 註冊觸控筆:初始無線連線 觸控筆和 OS 通訊完成註冊及同步時。
- 觸控取樣率 (硬體):觸控螢幕的每秒次數 檢查指標是否觸碰表面,範圍從 60 到 1000Hz。
- 輸入處理 (應用程式):套用色彩、圖形效果及轉換 使用者輸入內容時
- 圖形轉譯 (OS + 硬體):緩衝區互換、硬體處理。
低延遲圖形
Jetpack 低延遲圖形程式庫 減少使用者輸入內容與畫面算繪作業之間的處理時間。
程式庫會避免多緩衝區轉譯作業,並 採用前端緩衝區轉譯技術,也就是直接寫入 。
前端緩衝區轉譯
前端緩衝區是螢幕用於轉譯的記憶體。距離最近的 應用程式可以直接在螢幕上繪圖。低延遲程式庫能 直接轉譯至前端緩衝區。這麼做可提高成效 防止緩衝區互換,這種情況在一般多緩衝區轉譯作業中可能會發生 或雙緩衝區轉譯 (最常見的情況)。
雖然前端緩衝區轉譯是絕佳的 但不適合用來重新整理整個螢幕畫面。取代為 前端緩衝區轉譯,應用程式會將內容轉譯到緩衝區中 螢幕正在閱讀中。因此,系統可能會 出現錯誤或撕裂 (請見下方說明)。
低延遲程式庫適用於 Android 10 (API 級別 29) 以上版本 以及搭載 Android 10 (API 級別 29) 以上版本的 ChromeOS 裝置。
依附元件
低延遲程式庫提供了用於前端緩衝區轉譯的元件
。程式庫會新增為應用程式模組中的依附元件
build.gradle
檔案:
dependencies {
implementation "androidx.graphics:graphics-core:1.0.0-alpha03"
}
GLFrontBufferRenderer 回呼
低延遲程式庫包含了
GLFrontBufferRenderer.Callback
敬上
介面,其中定義下列方法:
低延遲程式庫與資料類型
GLFrontBufferRenderer
。
然而,程式庫會將資料處理為包含數百個資料點的資料流; 因此在設計資料時,應針對記憶體用量和分配進行最佳化調整。
回呼
如要啟用轉譯回呼,請實作 GLFrontBufferedRenderer.Callback
和
覆寫 onDrawFrontBufferedLayer()
和 onDrawDoubleBufferedLayer()
。
GLFrontBufferedRenderer
會使用回呼,在
盡可能提高安全性
val callback = object: GLFrontBufferedRenderer.Callback<DATA_TYPE> {
override fun onDrawFrontBufferedLayer(
eglManager: EGLManager,
bufferInfo: BufferInfo,
transform: FloatArray,
param: DATA_TYPE
) {
// OpenGL for front buffer, short, affecting small area of the screen.
}
override fun onDrawMultiDoubleBufferedLayer(
eglManager: EGLManager,
bufferInfo: BufferInfo,
transform: FloatArray,
params: Collection<DATA_TYPE>
) {
// OpenGL full scene rendering.
}
}
宣告 GLFrontBufferedRenderer 例項
提供 SurfaceView
和GLFrontBufferedRenderer
回呼函式。GLFrontBufferedRenderer
會將算繪作業最佳化
,接著使用回呼:
var glFrontBufferRenderer = GLFrontBufferedRenderer<DATA_TYPE>(surfaceView, callbacks)
轉譯
當您呼叫
renderFrontBufferedLayer()
敬上
方法,觸發 onDrawFrontBufferedLayer()
回呼。
當您呼叫
commit()
敬上
函式,觸發 onDrawMultiDoubleBufferedLayer()
回呼。
在下方範例中,程序會轉譯至前端緩衝區 (快速
使用者開始在螢幕上繪圖 (ACTION_DOWN
) 並移動時
指標 (ACTION_MOVE
)。程序會轉譯為雙緩衝區
當指標離開螢幕表面 (ACTION_UP
) 時。
別擔心!您可以使用
requestUnbufferedDispatch()
敬上
要求輸入系統不要批次處理動作事件,而是
功能推出時,您可以:
when (motionEvent.action) {
MotionEvent.ACTION_DOWN -> {
// Deliver input events as soon as they arrive.
view.requestUnbufferedDispatch(motionEvent)
// Pointer is in contact with the screen.
glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE)
}
MotionEvent.ACTION_MOVE -> {
// Pointer is moving.
glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE)
}
MotionEvent.ACTION_UP -> {
// Pointer is not in contact in the screen.
glFrontBufferRenderer.commit()
}
MotionEvent.CANCEL -> {
// Cancel front buffer; remove last motion set from the screen.
glFrontBufferRenderer.cancel()
}
}
轉譯的注意事項
在螢幕的一小塊區域中進行手寫、繪圖和素描。
在全螢幕模式下更新、平移及縮放內容。這麼做可能導致畫面撕裂。
撕裂
當螢幕在重新整理時,畫面緩衝區出現撕裂 同時修改兩者。螢幕畫面的一部分顯示新資料,另一個部分則顯示 顯示舊資料
動作預測
Jetpack 動作預測 程式庫減少 可預估使用者的筆觸路徑,並提供暫時性的 以人工方式指向轉譯器
動作預測程式庫會以 MotionEvent
物件形式接收使用者的實際輸入內容。
這些物件中包含 x 和 y 座標、壓力和時間
動態預測器所運用的這些結果來預測未來的MotionEvent
如需儲存大量結構化物件
建議使用 Cloud Bigtable
預測的 MotionEvent
物件只是預估值。預測事件能減少
使用者感知的延遲時間,但必須以實際的 MotionEvent
取代預測的資料
再加以接收
動作預測程式庫適用於 Android 4.4 (API 級別 19) 版和 以及搭載 Android 9 (API 級別 28) 以上版本的 ChromeOS 裝置。
依附元件
動作預測程式庫提供預測實作。
程式庫會新增為應用程式模組 build.gradle
檔案中的依附元件:
dependencies {
implementation "androidx.input:input-motionprediction:1.0.0-beta01"
}
實作
動作預測程式庫包含了
MotionEventPredictor
敬上
介面,其中定義下列方法:
宣告 MotionEventPredictor
的例項
var motionEventPredictor = MotionEventPredictor.newInstance(view)
為預測程序提供資料
motionEventPredictor.record(motionEvent)
預測
when (motionEvent.action) {
MotionEvent.ACTION_MOVE -> {
val predictedMotionEvent = motionEventPredictor?.predict()
if(predictedMotionEvent != null) {
// use predicted MotionEvent to inject a new artificial point
}
}
}
動作預測的注意事項
在新增預測點後移除舊的預測點。
請勿使用預測點進行最終算繪。
筆記應用程式
ChromeOS 可宣告應用程式具有某些筆記相關功能。
如要在 ChromeOS 上將應用程式註冊為筆記應用程式,請參閱「輸入 相容性。
如要在 Android 上註冊應用程式做為記事,請參閱建立記事 app。
Android 14 (API 級別 34) 導入了
ACTION_CREATE_NOTE
敬上
意圖,可讓應用程式在鎖定時啟動筆記活動
。
透過 ML Kit 辨識數位墨水
使用 ML Kit 數位墨水 辨識, 您的應用程式可以辨識數位表面的手寫文字 語言。也可以對素描進行分類。
ML Kit 提供
Ink.Stroke.Builder
敬上
類別,用來建立可由機器學習模型處理的 Ink
物件
將手寫內容轉換成文字
除了手寫辨識外,模型還能 手勢 例如刪除和圈選
請參閱數位墨水 辨識 。