Пользовательские текстовые редакторы

Пользовательские текстовые редакторы — это представления, которые не являются компонентами EditText или текстовыми виджетами WebView , но, тем не менее, поддерживают ввод текста путем реализации обратного вызова onCreateInputConnection() , который вызывается, когда представление находится в фокусе и система запрашивает InputConnection для представления.

Вызов onCheckIsTextEditor() из пользовательского текстового редактора должен возвращать true .

Поддержка рукописного ввода стилусом в пользовательских текстовых редакторах.

Android 14 (уровень API 34) и более поздние версии по умолчанию поддерживают ввод стилусом в стандартных компонентах ввода текста Android (см. Ввод стилусом в текстовые поля ). Однако настраиваемые поля ввода текста (или редакторы) требуют дополнительной разработки.

Чтобы создать собственный текстовый редактор, выполните следующие действия:

  1. Включить инициацию рукописного ввода
  2. Объявить поддержку рукописного ввода
  3. Поддержка жестов рукописного ввода (выбор, удаление, вставка и т. д.)
  4. Предоставление местоположения курсора и других данных о положении в IME.
  5. Показывать значок рукописного ввода стилуса при наведении

Включить инициацию рукописного ввода

Если представление состоит исключительно из одного текстового редактора, система представления может автоматически инициировать рукописный ввод стилуса для представления. В противном случае представление должно реализовать собственную логику инициации рукописного ввода.

Автоматическое начало рукописного ввода

Если представление отображает один текстовый редактор и никакого другого контента, представление может включить автоматическую инициацию рукописного ввода системы представления, вызвав setAutoHandwritingEnabled(true) .

Если включен автоматический рукописный ввод, движение стилуса, начинающееся в любом месте в пределах рукописного ввода, автоматически запускает режим рукописного ввода. Редактор метода ввода ( IME ) получает события движения стилуса и фиксирует распознанный текст.

Поле ввода с окружающим прямоугольником, обозначающим границы обнаружения событий движения стилуса.
Рисунок 1. Рукописный ввод в пределах поля EditText .

Пользовательское начало рукописного ввода

Если представление содержит несколько текстовых редакторов или контент в дополнение к одному текстовому редактору, представление должно реализовать свою собственную логику инициации рукописного ввода следующим образом:

  1. Отключите автоматическое инициирование рукописного ввода в системе просмотра, вызвав setAutoHandwritingEnabled(false) .

  2. Отслеживайте все текстовые редакторы, которые видны в представлении.

  3. Отслеживайте события движения, полученные представлением, в dispatchTouchEvent() .

    • Когда движение стилуса происходит в пределах рукописного ввода текстового редактора, сфокусируйте текстовый редактор (если он еще не сфокусирован).

    • Если редактор еще не был сфокусирован, перезапустите IME редактора с новым содержимым, вызвав InputMethodManager#restartInput() .

    • Запустите сеанс рукописного ввода стилусом, вызвав InputMethodManager#startStylusHandwriting() .

Если текстовый редактор находится внутри прокручиваемого представления, движение стилуса в пределах рукописного ввода редактора следует считать рукописным вводом, а не прокруткой. Используйте ViewParent#requestDisallowInterceptTouchEvent() чтобы запретить прокручиваемому представлению предка перехватывать события касания из текстового редактора.

Детали API

  • MotionEvent#getToolType() — указывает, исходит ли MotionEvent от стилуса; в этом случае возвращаемым значением является TOOL_TYPE_STYLUS или TOOL_TYPE_ERASER .

  • InputMethodManager#isStylusHandwritingAvailable() — указывает, поддерживает ли IME рукописный ввод с помощью стилуса. Вызывайте этот метод перед каждым вызовом InputMethodManager#startStylusHandwriting() поскольку доступность рукописного ввода могла измениться.

  • InputMethodManager#startStylusHandwriting() — переводит IME в режим рукописного ввода. Событие движения ACTION_CANCEL отправляется в приложение для отмены текущего жеста. События движения стилуса больше не отправляются в приложение.

    События движения стилуса текущего жеста, которые уже были отправлены в приложение, пересылаются в IME. IME необходим для отображения окна рукописного пера, через которое IME получает все последующие объекты MotionEvent . IME фиксирует распознанный рукописный текст с помощью API-интерфейсов InputConnection .

    Если IME не может перейти в режим рукописного ввода, вызов этого метода не выполняется.

Объявить поддержку рукописного ввода

При заполнении аргумента EditorInfo View#onCreateInputConnection(EditorInfo) вызовите setStylusHandwritingEnabled() чтобы сообщить IME, что текстовый редактор поддерживает рукописный ввод. Объявите поддерживаемые жесты с помощью setSupportedHandwritingGestures() и setSupportedHandwritingGesturePreviews() .

Поддержка жестов рукописного ввода

IME могут поддерживать различные жесты рукописного ввода, например обведение текста для его выбора или написание текста для его удаления.

Рисунок 2. Круг для выбора текста.
Рисунок 3. Набросок для удаления текста.

Пользовательские редакторы реализуют InputConnection#performHandwritingGesture() и InputConnection#previewHandwritingGesture() для поддержки различных типов HandwritingGesture , таких как SelectGesture , DeleteGesture и InsertGesture .

Объявите поддерживаемые жесты рукописного ввода при заполнении аргумента EditorInfo View#onCreateInputConnection(EditorInfo) (см. раздел «Объявление поддержки рукописного ввода» ).

Детали API

  • InputConnection#performHandwritingGesture(HandwritingGesture, Executor, IntConsumer) — реализует жесты. Аргумент HandwritingGesture содержит информацию о местоположении, которую можно использовать, чтобы определить, где в тексте следует выполнить жест. Например, SelectGesture предоставляет объект RectF , определяющий выбранный текстовый диапазон, а InsertGesture предоставляет объект PointF , определяющий смещение текста, по которому вставляется текст.

    Используйте параметры Executor и IntConsumer для отправки обратно результата операции. Если предоставлены аргументы как исполнителя, так и потребителя, используйте исполнителя для вызова IntConsumer#accept() , например:

    
    executor.execute { consumer.accept(HANDWRITING_GESTURE_RESULT_SUCCESS) }
    
  • HandwritingGesture#getFallbackText() — предоставляет резервный текст, который IME фиксирует в позиции курсора, если под областью рукописного жеста нет подходящего текста.

    Иногда IME не может определить, предназначен ли жест стилуса для выполнения операции жеста или для ввода текста от руки. Пользовательский текстовый редактор отвечает за определение намерения пользователя и выполнение соответствующего действия (в зависимости от контекста) в месте жеста.

    Например, если IME не может определить, намеревался ли пользователь нарисовать курсор ⋁, выполнить жест вставки пробела или написать от руки букву «v», IME может отправить InsertGesture с резервным текстом «v».

    Редактор должен сначала попытаться выполнить жест вставки пробела. Если жест невозможно выполнить (например, в указанном месте нет текста), редактору следует вернуться к вставке буквы «v» в позиции курсора.

  • InputConnection#previewHandwritingGesture(PreviewableHandwritingGesture, CancellationSignal) — выполняет предварительный просмотр текущего жеста. Например, когда пользователь начинает рисовать круг вокруг некоторого текста, может отображаться предварительный просмотр полученного выделения в реальном времени и постоянно обновляться по мере того, как пользователь продолжает рисовать. Только определенные типы жестов доступны для предварительного просмотра (см. PreviewableHandwritingGesture ).

    Параметр CancellationSignal может использоваться IME для отмены предварительного просмотра. Если другие события нарушают предварительный просмотр (например, текст изменяется программно или возникают новые команды 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) чтобы получить доступ к системному значку рукописного ввода стилуса.

Дополнительные ресурсы