Editores de texto personalizados

Los editores de texto personalizados son vistas que no son componentes EditText ni widgets de texto WebView, pero que, sin embargo, admiten entradas de texto implementando la devolución de llamada onCreateInputConnection(), a la que se llama cuando una vista está enfocada y el sistema solicita un InputConnection para la vista.

Una llamada a onCheckIsTextEditor() desde un editor de texto personalizado debe mostrar true.

Compatibilidad con la escritura a mano con pluma stylus en editores de texto personalizados

Android 14 (nivel de API 34) y las versiones posteriores admiten la entrada de la pluma stylus en los componentes de entrada de texto estándar de Android de forma predeterminada (consulta Entrada de la pluma stylus en los campos de texto). Sin embargo, los campos (o editores) de entrada de texto personalizados requieren un desarrollo adicional.

Para crear un editor de texto personalizado, haz lo siguiente:

  1. Habilitar el inicio de escritura a mano
  2. Cómo declarar la compatibilidad con la escritura a mano
  3. Admitir gestos de escritura a mano (seleccionar, borrar, insertar, etcétera)
  4. Cómo proporcionar la ubicación del cursor y otros datos de posición al IME
  5. Mostrar el ícono de desplazamiento de escritura a mano con la pluma stylus

Habilitar el inicio de escritura a mano

Si una vista consta solo de un editor de texto, el sistema de vistas puede iniciar automáticamente la escritura a mano con la pluma stylus para la vista. De lo contrario, la vista debe implementar su propia lógica de inicio de escritura a mano.

Iniciación automática de escritura a mano

Si una vista muestra un solo editor de texto y ningún otro contenido, puede habilitar la iniciación automática de escritura a mano del sistema de vistas llamando a setAutoHandwritingEnabled(true).

Con la escritura a mano automática habilitada, el movimiento de la pluma stylus que comienza en cualquier lugar dentro de los límites de escritura a mano de la vista inicia automáticamente el modo de escritura a mano. El editor de método de entrada (IME) recibe los eventos de movimiento de la pluma stylus y confirma el texto reconocido.

Campo de entrada con el rectángulo circundante que indica los límites para la detección de eventos de movimiento de la pluma stylus.
Figura 1: Escritura a mano dentro de los límites de un campo EditText.

Iniciación de escritura a mano personalizada

Si una vista incluye varios editores de texto o contenido, además de uno, debe implementar su propia lógica de inicio de escritura a mano de la siguiente manera:

  1. Inhabilita el inicio automático de escritura a mano del sistema de vista llamando a setAutoHandwritingEnabled(false).

  2. Realiza un seguimiento de todos los editores de texto que están visibles en la vista.

  3. Supervisa los eventos de movimiento que recibe la vista en dispatchTouchEvent().

    • Cuando se produce el movimiento de la pluma stylus dentro de los límites de escritura a mano de un editor de texto, enfoca el editor de texto (si aún no está enfocado).

    • Si el editor no estaba enfocado, reinicia el IME del editor con contenido nuevo llamando a InputMethodManager#restartInput().

    • Para iniciar la sesión de escritura a mano con la pluma stylus, llama a InputMethodManager#startStylusHandwriting().

Si un editor de texto está dentro de una vista desplazable, el movimiento de la pluma stylus dentro de los límites de escritura a mano del editor se debe considerar escritura a mano, no desplazamiento. Usa ViewParent#requestDisallowInterceptTouchEvent() para evitar que una vista principal desplazable intercepte eventos táctiles de un editor de texto.

Detalles de la API

  • MotionEvent#getToolType(): Indica si el objeto MotionEvent es de una pluma stylus, en cuyo caso el valor que se muestra es TOOL_TYPE_STYLUS o TOOL_TYPE_ERASER.

  • InputMethodManager#isStylusHandwritingAvailable(): Indica si el IME admite la escritura a mano con pluma stylus. Llama a este método antes de cada llamada a InputMethodManager#startStylusHandwriting(), ya que la disponibilidad de escritura a mano puede haber cambiado.

  • InputMethodManager#startStylusHandwriting(): Hace que el IME ingrese al modo de escritura a mano. Se envía un evento de movimiento ACTION_CANCEL a la app para cancelar el gesto actual. Ya no se envían a la app los eventos de movimiento de la pluma stylus.

    Los eventos de movimiento de la pluma stylus del gesto actual que ya se enviaron a la app se reenvían al IME. Se requiere el IME para mostrar una ventana de tinta de la pluma stylus a través de la cual el IME recibe todos los objetos MotionEvent siguientes. El IME confirma el texto de escritura a mano reconocido mediante las APIs de InputConnection.

    Si el IME no puede ingresar al modo de escritura a mano, esta llamada de método es una no-op.

Cómo declarar la compatibilidad con la escritura a mano

Cuando completes el argumento EditorInfo de View#onCreateInputConnection(EditorInfo), llama a setStylusHandwritingEnabled() para informar al IME que el editor de texto admite la escritura a mano. Declara los gestos compatibles con setSupportedHandwritingGestures() y setSupportedHandwritingGesturePreviews().

Compatibilidad con gestos de escritura a mano

Los IME pueden admitir varios gestos de escritura a mano, como encerrar texto en un círculo para seleccionarlo o garabatear texto para borrarlo.

Figura 2: Haz un círculo para seleccionar texto.
Figura 3: Haz un garabato para borrar texto.

Los editores personalizados implementan InputConnection#performHandwritingGesture() y InputConnection#previewHandwritingGesture() para admitir diferentes tipos de HandwritingGesture, como SelectGesture, DeleteGesture y InsertGesture.

Declara gestos de escritura a mano admitidos cuando completes el argumento EditorInfo de View#onCreateInputConnection(EditorInfo) (consulta la sección Cómo declarar la compatibilidad con la escritura a mano).

Detalles de la API

  • InputConnection#performHandwritingGesture(HandwritingGesture, Executor, IntConsumer): Implementa gestos. El argumento HandwritingGesture contiene información de ubicación que puedes usar para determinar en qué parte del texto realizar el gesto. Por ejemplo, SelectGesture proporciona un objeto RectF que especifica el rango de texto seleccionado, y InsertGesture proporciona un objeto PointF que especifica el desplazamiento de texto en el que se insertará texto.

    Usa los parámetros Executor y IntConsumer para enviar el resultado de la operación. Cuando se proporcionan los argumentos del ejecutor y del consumidor, usa el ejecutor para llamar a IntConsumer#accept(), por ejemplo:

    
    executor.execute { consumer.accept(HANDWRITING_GESTURE_RESULT_SUCCESS) }
    
    
  • HandwritingGesture#getFallbackText(): Proporciona texto de resguardo que confirma el IME en la posición del cursor si no hay texto aplicable debajo del área de un gesto de escritura a mano.

    A veces, el IME no puede determinar si un gesto de la pluma stylus está destinado a realizar una operación gestual o escribir texto a mano. Un editor de texto personalizado es responsable de determinar la intención del usuario y realizar la acción adecuada (según el contexto) en la ubicación del gesto.

    Por ejemplo, si el IME no puede determinar si el usuario quería dibujar un signo de intercalación hacia abajo ⋁ para realizar un gesto de inserción de espacio o escribir a mano la letra "v", el IME puede enviar una InsertGesture con texto alternativo "v".

    El editor primero debe intentar realizar el gesto de inserción de espacio. Si no se puede realizar el gesto (por ejemplo, no hay texto en la ubicación especificada), el editor debe volver a insertar "v" en la posición del cursor.

  • InputConnection#previewHandwritingGesture(PreviewableHandwritingGesture, CancellationSignal): Muestra una vista previa de un gesto en curso. Por ejemplo, cuando el usuario comienza a dibujar un círculo alrededor de un texto, se puede mostrar una vista previa en vivo de la selección resultante y actualizarla de forma continua a medida que el usuario sigue dibujando. Solo se puede obtener una vista previa de ciertos tipos de gestos (consulta PreviewableHandwritingGesture).

    El IME puede usar el parámetro CancellationSignal para cancelar la vista previa. Si otros eventos interrumpen la vista previa (por ejemplo, el texto se cambia de manera programática o se producen nuevos comandos InputConnection), el editor personalizado puede cancelar la vista previa.

    Los gestos de vista previa son solo para visualización y no deben cambiar el estado del editor. Por ejemplo, una vista previa de SelectGesture oculta el rango de selección actual del editor y destaca el rango de vista previa de gestos. Sin embargo, una vez que se cancela la vista previa, el editor debería restablecer su rango de selección anterior.

Cómo proporcionar la ubicación del cursor y otros datos de posición

En el modo de escritura a mano, el IME puede solicitar la ubicación del cursor y otros datos de posición mediante InputConnection#requestCursorUpdates(). El editor personalizado responde con una llamada a InputMethodManager#updateCursorAnchorInfo(View, CursorAnchorInfo). Los datos de CursorAnchorInfo relevantes para la escritura a mano con la pluma stylus se proporcionan a través de los siguientes métodos CursorAnchorInfo.Builder:

  • setInsertionMarkerLocation(): Establece la ubicación del cursor. El IME usa el valor para animar la tinta de escritura a mano a la ubicación del cursor.
  • setEditorBoundsInfo(): Establece los límites del editor y de la escritura a mano. El IME usa estos datos para posicionar la barra de herramientas de escritura a mano del IME en la pantalla.
  • addVisibleLineBounds(): Establece los límites de todas las líneas de texto visibles (o parcialmente visibles) del editor. El IME usa los límites de línea para mejorar la precisión en el reconocimiento de gestos de escritura a mano.
  • setTextAppearanceInfo(): Establece la apariencia del texto con información derivada del campo de entrada de texto. El IME usa la información para darle estilo a la tinta de escritura a mano.

Mostrar el ícono de desplazamiento de escritura a mano con la pluma stylus

Muestra el ícono de colocar el cursor sobre la escritura a mano con la pluma stylus cuando esta se coloque sobre los límites de escritura a mano de tu editor de texto personalizado y el IME seleccionado admita la escritura a mano con pluma stylus (InputMethodManager#isStylusHandwritingAvailable()).

Anula View#onResolvePointerIcon() para obtener un ícono de desplazamiento para escribir a mano con la pluma stylus. En la anulación, llama a PointerIcon.getSystemIcon(context, PointerIcon.TYPE_HANDWRITING) para acceder al ícono de colocar el cursor sobre la escritura a mano de la pluma stylus del sistema.

Recursos adicionales