自定义文本编辑器

自定义文本编辑器是 EditText 组件或 WebView 个文本 widget, 不过,通过实现 onCreateInputConnection() 回调,在视图获得焦点并且系统请求 InputConnection 视图

onCheckIsTextEditor() 应返回 true

在自定义文本编辑器中支持触控笔手写

Android 14(API 级别 34)及更高版本支持标准 Android 中的触控笔输入 默认使用文本输入组件(请参阅文本中的触控笔输入 字段)。 不过,自定义文本输入字段(或编辑器)需要进行额外的开发。

如需创建自定义文本编辑器,请执行以下操作:

  1. 启用手写功能
  2. 声明手写支持
  3. 支持手写手势(选择、删除、插入等)
  4. 向 IME 提供光标位置和其他位置数据
  5. 显示触控笔手写悬停图标

启用手写功能

如果一个视图仅由一个文本编辑器组成,则视图系统可以 自动为视图启动触控笔手写功能。否则,视图必须 实现自己的手写启动逻辑。

自动开始手写

如果视图只显示一个文本编辑器而没有其他内容,则该视图可以选择 通过调用 View 系统的自动手写启动, setAutoHandwritingEnabled(true)

启用自动手写功能后,触控笔动作会从视图内的任意位置开始 手写边界会自动启动手写模式。输入法 编辑器(IME)接收 触控笔动作事件并提交识别出的文本。

包含矩形的输入字段,用于指示触控笔动作事件的检测边界。
图 1. EditText 字段内的手写内容。

自定义手写功能

如果视图除了单个文本之外,还包含多个文本编辑器或内容 编辑器,视图必须实现自己的手写启动逻辑,如下所示:

  1. 通过调用 setAutoHandwritingEnabled(false)

  2. 跟踪视图内显示的所有文本编辑器。

  3. dispatchTouchEvent()

如果文本编辑器位于可滚动视图内,触控笔在 的手写边界应被视为手写,而不是滚动。使用 ViewParent#requestDisallowInterceptTouchEvent() 防止可滚动祖先视图拦截来自文本的触摸事件 编辑器。

API 详细信息

  • MotionEvent#getToolType() - 指示 MotionEvent 是否来自触控笔(如果是的话) 返回值是 TOOL_TYPE_STYLUSTOOL_TYPE_ERASER

  • InputMethodManager#isStylusHandwritingAvailable() - 指示 IME 是否支持触控笔手写。调用 方法,然后再在每次调用 InputMethodManager#startStylusHandwriting() 之前调用该方法。 因为手写功能的可用性可能已更改

  • InputMethodManager#startStylusHandwriting() — 使 IME 进入手写模式。一个 ACTION_CANCEL 动作事件会分派给应用,以取消当前手势。触控笔 不再将动作事件分派给应用。

    已分派给的当前手势的触控笔动作事件 系统会将应用转发到 IME。必须输入 IME 才能显示触控笔墨水 IME 通过该窗口接收所有后续 MotionEvent 对象。 IME 使用 InputConnection API。

    如果 IME 无法进入手写模式,则此方法调用为空操作。

声明手写支持

在填写 EditorInfo 参数 / View#onCreateInputConnection(EditorInfo) 次通话 setStylusHandwritingEnabled(),用于告知 IME 文本编辑器支持手写。 声明支持的手势: setSupportedHandwritingGestures()setSupportedHandwritingGesturePreviews()

支持手写手势

IME 可以支持各种手写手势,例如圈出文字以供选择 或在文字上乱涂即可将其删除。

图 2. 圈起即可选择文字。
<ph type="x-smartling-placeholder">
</ph> 。 <ph type="x-smartling-placeholder">
</ph> 图 3.在文字上乱涂即可删除文字。

实现自定义编辑器 InputConnection#performHandwritingGesture()InputConnection#previewHandwritingGesture() 来支持不同的 HandwritingGesture 类型,例如 SelectGestureDeleteGestureInsertGesture

在填充 EditorInfo 实参时声明支持的手写手势 最多显示 View#onCreateInputConnection(EditorInfo) 个(请参阅声明手写内容) 支持部分)。

API 详细信息

  • InputConnection#performHandwritingGesture(HandwritingGesture, Executor, IntConsumer) — 实现手势。HandwritingGesture 参数包含 位置信息,用于确定要在文字中的什么位置 执行相应手势例如,SelectGesture 提供了一个 RectF 对象, 用于指定选定的文本范围,InsertGesture 用于提供 PointF 对象 用于指定要插入文本的文本偏移量。

    使用 ExecutorIntConsumer 参数 返回操作结果。当执行器和 提供了使用方参数,则使用 Executor 来调用 IntConsumer#accept()、 例如:

    
    executor.execute { consumer.accept(HANDWRITING_GESTURE_RESULT_SUCCESS) }
    
    
  • HandwritingGesture#getFallbackText() - 提供 IME 在光标位置提交的后备文本(如果没有 相应文字位于手写手势区域下方。

    有时,IME 无法确定触控笔手势 用于执行手势操作或手写文本。自定义文本 编辑器负责确定用户的意图并执行 手势位置处的适当操作(具体取决于上下文)。

    例如,如果 IME 无法确定用户是不是想要绘制 向下插入符号 ⋁ 执行插入空格手势或手写 字母“v”IME 可以发送包含回退文本“v”的 InsertGesture

    编辑器应首先尝试执行插入空格的手势。如果 手势(例如,该地点没有文字) ),编辑器应回退为插入“v”光标 排名。

  • InputConnection#previewHandwritingGesture(PreviewableHandwritingGesture, CancellationSignal) — 预览正在进行的手势。例如,当用户开始绘制 画圈圈出一些文字,即可实时预览所选文字, 会随着用户继续绘制而持续显示和不断更新。仅特定 手势类型是可预览的(请参阅 PreviewableHandwritingGesture)。

    IME 可以使用 CancellationSignal 参数取消 预览。如果其他事件中断预览(例如文本被更改) 以编程方式或发生新的 InputConnection 命令时),自定义编辑器 可以取消预览。

    预览手势仅用于显示,不应更改编辑器的 状态。例如,SelectGesture 预览会隐藏编辑器的当前 选择范围,并突出显示手势预览范围。但是,一旦 预览被取消时,编辑器应恢复其之前的选择范围。

提供光标位置和其他位置数据

在手写模式下,IME 可以请求光标位置和其他位置数据 使用 InputConnection#requestCursorUpdates()。 自定义编辑器以调用 InputMethodManager#updateCursorAnchorInfo(View, CursorAnchorInfo)。 其中的数据 CursorAnchorInfo 与触控笔手写相关的实用信息, CursorAnchorInfo.Builder 方法:

  • setInsertionMarkerLocation() – 设置光标的位置。IME 使用该值添加动画效果 手写内容。
  • setEditorBoundsInfo() — 设置编辑器的边界和手写边界。IME 使用 此数据将 IME 的手写工具栏定位在屏幕上。
  • addVisibleLineBounds() — 设置所有可见(或部分可见)文本行的边界 编辑器。IME 使用行边界来提高识别准确性 以及手写手势
  • setTextAppearanceInfo() — 使用派生自文本的信息设置文本外观 输入字段。IME 使用该信息设置手写墨水样式。

显示触控笔手写悬停图标

当触控笔悬停在 自定义文本编辑器的手写边界,并且所选 IME 支持 触控笔手写 (InputMethodManager#isStylusHandwritingAvailable())。

替换 View#onResolvePointerIcon() 以获取触控笔手写的悬停图标。在替换项中,调用 PointerIcon.getSystemIcon(context, PointerIcon.TYPE_HANDWRITING) 访问系统的触控笔手写悬停图标。

其他资源