Custom text editors are views that are not
EditText
components or
WebView
text widgets but
nevertheless support text input by implementing the
onCreateInputConnection()
callback, which is called when a view is focused and the system requests an
InputConnection
for the view.
A call to
onCheckIsTextEditor()
from a custom text editor should return true
.
Support stylus handwriting in custom text editors
Android 14 (API level 34) and higher support stylus input in standard Android text entry components by default (see Stylus input in text fields). However, custom text entry fields (or editors) require additional development.
To create a custom text editor, do the following:
- Enable handwriting initiation
- Declare handwriting support
- Support handwriting gestures (select, delete, insert, and so on)
- Provide cursor location and other position data to the IME
- Show the stylus handwriting hover icon
Enable handwriting initiation
If a view consists solely of a single text editor, the view system can automatically initiate stylus handwriting for the view. Otherwise, the view must implement its own handwriting initiation logic.
Automatic handwriting initiation
If a view displays a single text editor and no other content, the view can opt
into the view system's automatic handwriting initiation by calling
setAutoHandwritingEnabled(true)
.
With auto handwriting enabled, stylus motion starting anywhere within the view's handwriting bounds automatically initiates handwriting mode. The input method editor (IME) receives the stylus motion events and commits the recognized text.
Custom handwriting initiation
If a view contains multiple text editors or content in addition to a single text editor, the view must implement its own handwriting initiation logic as follows:
Opt out of the view system's automatic handwriting initiation by calling
setAutoHandwritingEnabled(false)
.Keep track of all text editors that are visible within the view.
Monitor motion events received by the view in
dispatchTouchEvent()
.When stylus motion occurs within a text editor's handwriting bounds, focus the text editor (if not already focused).
If the editor was not already focused, restart the editor's IME with new contents by calling
InputMethodManager#restartInput()
.Start the stylus handwriting session by calling
InputMethodManager#startStylusHandwriting()
.
If a text editor is inside a scrollable view, stylus movement within the
editor's handwriting bounds should be considered handwriting, not scrolling. Use
ViewParent#requestDisallowInterceptTouchEvent()
to prevent a scrollable ancestor view from intercepting touch events from a text
editor.
API details
MotionEvent#getToolType()
— Indicates whether theMotionEvent
is from a stylus, in which case the return value isTOOL_TYPE_STYLUS
orTOOL_TYPE_ERASER
.InputMethodManager#isStylusHandwritingAvailable()
— Indicates whether the IME supports stylus handwriting. Call this method before every call toInputMethodManager#startStylusHandwriting()
since the handwriting availability may have changed.InputMethodManager#startStylusHandwriting()
— Causes the IME to enter handwriting mode. AnACTION_CANCEL
motion event is dispatched to the app to cancel the current gesture. Stylus motion events are no longer dispatched to the app.Stylus motion events of the current gesture that were already dispatched to the app are forwarded to the IME. The IME is required to show a stylus ink window through which the IME receives all following
MotionEvent
objects. The IME commits recognized handwriting text using theInputConnection
APIs.If the IME can't enter handwriting mode, this method call is a no-op.
Declare handwriting support
When filling in the
EditorInfo
argument
of
View#onCreateInputConnection(EditorInfo)
call
setStylusHandwritingEnabled()
to inform the IME that the text editor supports handwriting.
Declare supported gestures with
setSupportedHandwritingGestures()
and
setSupportedHandwritingGesturePreviews()
.
Support handwriting gestures
IMEs can support various handwriting gestures, such as circling text to select it or scribbling over text to delete it.
Custom editors implement
InputConnection#performHandwritingGesture()
and
InputConnection#previewHandwritingGesture()
to support different
HandwritingGesture
types, such as
SelectGesture
,
DeleteGesture
, and
InsertGesture
.
Declare supported handwriting gestures when filling in the EditorInfo
argument
of View#onCreateInputConnection(EditorInfo)
(see the Declare handwriting
support section).
API details
InputConnection#performHandwritingGesture(HandwritingGesture, Executor, IntConsumer)
— Implements gestures. TheHandwritingGesture
argument contains location information which you can use to determine where in the text to perform the gesture. For example,SelectGesture
provides aRectF
object that specifies the selected text range, andInsertGesture
provides aPointF
object that specifies the text offset at which to insert text.Use the
Executor
andIntConsumer
parameters to send back the result of the operation. When both the executor and consumer arguments are provided, use the executor to callIntConsumer#accept()
, for example:executor.execute { consumer.accept(HANDWRITING_GESTURE_RESULT_SUCCESS) }
HandwritingGesture#getFallbackText()
— Provides fallback text the IME commits at the cursor position if no applicable text is beneath the area of a handwriting gesture.Sometimes the IME is not able to determine whether a stylus gesture is intended to perform a gesture operation or to handwrite text. A custom text editor is responsible for determining the user's intention and performing the appropriate action (depending on context) at the gesture location.
For example, if the IME cannot ascertain whether the user meant to draw a downward caret ⋁ to perform an insert space gesture or to handwrite the letter "v," the IME can send an
InsertGesture
with fallback text "v".The editor should first try to perform the insert space gesture. If the gesture cannot be performed (for example, there is no text at the location specified), the editor should fall back to inserting "v" at the cursor position.
InputConnection#previewHandwritingGesture(PreviewableHandwritingGesture, CancellationSignal)
— Previews an ongoing gesture. For example, as the user begins to draw a circle around some text, a live preview of the resulting selection can be shown and continuously updated as the user continues drawing. Only certain gesture types are previewable (seePreviewableHandwritingGesture
).The
CancellationSignal
parameter can be used by the IME to cancel the preview. If other events disrupt the preview (for example, text is changed programmatically or newInputConnection
commands occur), the custom editor can cancel the preview.Preview gestures are for display only and shouldn't change the editor's state. For example, a
SelectGesture
preview hides the editor's current selection range and highlights the gesture preview range. But once the preview is canceled, the editor should restore its previous selection range.
Provide cursor location and other position data
In handwriting mode, the IME can request cursor location and other position data
using
InputConnection#requestCursorUpdates()
.
The custom editor responds with a call to
InputMethodManager#updateCursorAnchorInfo(View,
CursorAnchorInfo)
.
The data in
CursorAnchorInfo
relevant to stylus handwriting is provided through the following
CursorAnchorInfo.Builder
methods:
setInsertionMarkerLocation()
— Sets the location of the cursor. The IME uses the value to animate handwriting ink to the cursor location.setEditorBoundsInfo()
— Sets the editor's bounds and the handwriting bounds. The IME uses this data to position the IME's handwriting toolbar on screen.addVisibleLineBounds()
— Sets the bounds of all visible (or partially visible) text lines of the editor. The IME uses the line bounds to improve accuracy in recognizing handwriting gestures.setTextAppearanceInfo()
— Sets the text appearance with information derived from the text input field. The IME uses the information to style the handwriting ink.
Show the stylus handwriting hover icon
Display the stylus handwriting hover icon when the stylus hovers over the
handwriting bounds of your custom text editor and the selected IME supports
stylus handwriting
(InputMethodManager#isStylusHandwritingAvailable()
).
Override
View#onResolvePointerIcon()
to get a hover icon for stylus handwriting. In the override, call
PointerIcon.getSystemIcon(context, PointerIcon.TYPE_HANDWRITING)
to access the system's stylus handwriting hover icon.