自訂文字編輯器

自訂文字編輯器是非 EditText 元件或 WebView 文字小工具的檢視畫面,但都無法藉由實作 onCreateInputConnection() 回呼來支援文字輸入。系統會在聚焦檢視畫面時呼叫此回呼,讓系統為檢視畫面要求 InputConnection

從自訂文字編輯器呼叫 onCheckIsTextEditor() 時應傳回 true

支援自訂文字編輯器中的觸控筆手寫功能

根據預設,Android 14 (API 級別 34) 以上版本支援標準 Android 文字輸入元件中的觸控筆輸入功能 (請參閱「文字欄位中的觸控筆輸入內容」)。但自訂文字輸入欄位 (或編輯器) 需要額外開發。

如要建立自訂文字編輯器,請執行下列步驟:

  1. 啟用手寫功能
  2. 宣告手寫支援
  3. 支援手寫手勢 (選取、刪除、插入等)
  4. 將遊標位置和其他位置資料提供給輸入法編輯器
  5. 顯示觸控筆手寫懸停圖示

啟用手寫功能

如果檢視畫面只包含單一文字編輯器,檢視畫面系統可以自動為該檢視畫面啟動觸控筆手寫功能。否則,檢視畫面必須實作自己的手寫啟動邏輯。

自動啟動手寫輸入功能

如果檢視畫面顯示單一文字編輯器,但沒有其他內容,檢視畫面可以呼叫 setAutoHandwritingEnabled(true),選擇在檢視畫面系統自動進行手寫輸入。

啟用自動手寫功能後,從檢視畫面手寫邊界的任何位置開始,觸控筆動作就會自動啟動手寫模式。輸入法編輯器 (IME) 會接收觸控筆動作事件,並提交已辨識的文字。

包含矩形的輸入欄位,用來表示偵測觸控筆動作事件的邊界。
圖 1.EditText 欄位邊界內手寫輸入。

自訂手寫功能

如果檢視畫面除了單一文字編輯器以外,還包含多個文字編輯器或內容,檢視畫面必須按照下列方式實作自己的手寫啟動邏輯:

  1. 呼叫 setAutoHandwritingEnabled(false),關閉檢視畫面系統的自動手寫功能啟動功能。

  2. 追蹤在檢視畫面中顯示的所有文字編輯器。

  3. 監控 dispatchTouchEvent() 中檢視畫面接收的動作事件。

如果文字編輯器位於可捲動的檢視畫面中,請將編輯器手寫範圍中的觸控筆移動視為手寫輸入,而非捲動。使用 ViewParent#requestDisallowInterceptTouchEvent() 可避免可捲動的祖系檢視畫面攔截文字編輯器的觸控事件。

API 詳細資料

  • MotionEvent#getToolType() — 指出 MotionEvent 是否來自觸控筆,在這種情況下,回傳值為 TOOL_TYPE_STYLUSTOOL_TYPE_ERASER

  • InputMethodManager#isStylusHandwritingAvailable() — 指出輸入法編輯器是否支援觸控筆手寫。由於手寫可用性可能已經變更,因此每次呼叫 InputMethodManager#startStylusHandwriting() 前,都應呼叫這個方法。

  • InputMethodManager#startStylusHandwriting():讓輸入法編輯器進入手寫模式。系統會將 ACTION_CANCEL 動作事件分派給應用程式,以取消目前的手勢。系統不再將觸控筆動作事件分派給應用程式。

    對於已分派至應用程式的目前手勢,觸控筆動作事件會轉送到 IME。必須提供 IME,以便顯示觸控筆墨水視窗,輸入法編輯器接收以下所有 MotionEvent 物件。IME 修訂版本會使用 InputConnection API 辨識手寫文字。

    如果 IME 無法進入手寫模式,這個方法呼叫便為免人工管理。

宣告手寫支援

填入 View#onCreateInputConnection(EditorInfo)EditorInfo 引數時呼叫 setStylusHandwritingEnabled(),通知 IME 文字編輯器支援手寫功能。使用 setSupportedHandwritingGestures()setSupportedHandwritingGesturePreviews() 宣告支援的手勢。

支援手寫手勢

IME 可以支援多種手寫手勢,例如透過旋轉文字選取該手勢,或透過塗鴉的方式刪除文字即可刪除手勢。

圖 2. 圓圈即可選取文字。
圖 3.劃掉文字即可刪除文字。

自訂編輯器會實作 InputConnection#performHandwritingGesture()InputConnection#previewHandwritingGesture() 來支援不同的 HandwritingGesture 類型,例如 SelectGestureDeleteGestureInsertGesture

在填入 View#onCreateInputConnection(EditorInfo)EditorInfo 引數時,宣告支援的手寫手勢 (請參閱「宣告手寫支援」一節)。

API 詳細資料

  • InputConnection#performHandwritingGesture(HandwritingGesture, Executor, IntConsumer):實作手勢。HandwritingGesture 引數包含位置資訊,可用來判斷文字中的位置,以便執行手勢。舉例來說,SelectGesture 提供 RectF 物件來指定所選文字範圍,InsertGesture 提供 PointF 物件,用於指定文字偏移位置。

    使用 ExecutorIntConsumer 參數,傳回作業結果。如果同時提供執行程式和消費端引數,請使用執行程式呼叫 IntConsumer#accept(),例如:

    
    executor.execute { consumer.accept(HANDWRITING_GESTURE_RESULT_SUCCESS) }
    
    
  • HandwritingGesture#getFallbackText():如果手寫手勢區域下方沒有任何適用文字,系統會於遊標位置提供 IME 修訂版本的備用文字。

    有時 IME 無法判斷觸控筆手勢是否用於執行手勢作業或手寫文字。自訂文字編輯器負責判斷使用者的意圖,並在手勢位置採取適當動作 (視情境而定)。

    舉例來說,如果輸入法編輯器無法確定使用者是否打算繪製向下插入空格 ⋁,執行插入空格手勢或手寫字母「v」,則輸入法編輯器可以傳送含有備用文字「v」的 InsertGesture

    編輯器應先嘗試執行插入空格手勢。如果無法執行手勢 (例如,指定位置沒有任何文字),編輯器應改回在遊標位置插入「v」。

  • InputConnection#previewHandwritingGesture(PreviewableHandwritingGesture, CancellationSignal):預覽進行中的手勢。舉例來說,當使用者開始在某段文字周圍繪製圓形時,系統會在使用者繼續繪圖時,顯示即時預覽結果預覽並持續更新。只有特定手勢類型可供預覽 (請參閱 PreviewableHandwritingGesture)。

    IME 可使用 CancellationSignal 參數取消預覽。如果其他事件導致預覽作業中斷 (例如以程式輔助方式變更文字,或出現新的 InputConnection 指令),自訂編輯器可能會取消預覽。

    預覽手勢僅供顯示,不應變更編輯器的狀態。舉例來說,SelectGesture 預覽會隱藏編輯器目前的選取範圍,並醒目顯示手勢預覽範圍。不過,一旦取消預覽,編輯器就應還原先前的選取範圍。

提供遊標位置和其他位置資料

在手寫模式中,輸入法編輯器可以使用 InputConnection#requestCursorUpdates() 要求遊標位置和其他位置資料。自訂編輯器會呼叫 InputMethodManager#updateCursorAnchorInfo(View, CursorAnchorInfo)。與觸控筆手寫相關的 CursorAnchorInfo 資料會透過下列 CursorAnchorInfo.Builder 方法提供:

  • setInsertionMarkerLocation():設定遊標位置。IME 會使用這個值,為手寫墨水建立動畫效果至遊標位置。
  • setEditorBoundsInfo():設定編輯器的邊界和手寫邊界。IME 會使用這項資料,將輸入法編輯器的手寫工具列放在螢幕上。
  • addVisibleLineBounds():設定編輯器所有可見 (或部分顯示) 文字行的邊界。IME 會使用線條邊界來改善辨識手寫手勢的準確度。
  • setTextAppearanceInfo():使用衍生文字輸入欄位的資訊,設定文字外觀。輸入法編輯器會根據這項資訊設定手寫手寫樣式的樣式。

顯示觸控筆手寫懸停圖示

當觸控筆懸停在自訂文字編輯器的手寫範圍上,且所選的輸入法編輯器支援觸控筆手寫時,即可顯示觸控筆手寫懸停圖示 (InputMethodManager#isStylusHandwritingAvailable())。

覆寫 View#onResolvePointerIcon(),取得觸控筆手寫的懸停圖示。在覆寫中,呼叫 PointerIcon.getSystemIcon(context, PointerIcon.TYPE_HANDWRITING) 即可存取系統觸控筆手寫懸停圖示。

其他資源