Compatibilidad con funciones avanzadas de la pluma stylus

La pluma stylus les permite a los usuarios interactuar con las apps de forma cómoda y con una precisión absoluta al momento de tomar notas, dibujar y trabajar con apps de productividad, así como relajarse y divertirse con apps de juegos y entretenimiento.

Android y ChromeOS ofrecen una variedad de APIs para crear una experiencia excepcional con la pluma stylus en las apps. La clase MotionEvent proporciona información sobre la interacción del usuario con la pantalla, incluida la presión de la pluma stylus, la orientación, la inclinación, el desplazamiento y la detección de la palma. Las bibliotecas de gráficos de baja latencia y predicción de movimiento mejoran la renderización en pantalla de la pluma stylus para proporcionar una experiencia natural similar a la del bolígrafo y el papel.

MotionEvent

La clase MotionEvent representa las interacciones de entrada del usuario, como la posición y el movimiento de los punteros táctiles en la pantalla. Para la entrada de la pluma stylus, MotionEvent también expone los datos de presión, orientación, inclinación y desplazamiento.

Datos de eventos

Para acceder a datos de MotionEvent en apps basadas en vistas, configura un elemento onTouchListener:

Kotlin

val onTouchListener = View.OnTouchListener { view, event ->
  // Process motion event.
}

Java

View.OnTouchListener listener = (view, event) -> {
  // Process motion event.
};

El objeto de escucha recibe objetos MotionEvent del sistema para que tu app puede procesarlos.

Un objeto MotionEvent proporciona datos relacionados con los siguientes aspectos de un evento de la IU:

  • Acciones: interacción física con el dispositivo (tocar la pantalla, mover un puntero sobre la superficie de la pantalla, colocar el puntero sobre ella)
  • Punteros: identificadores de objetos que interactúan con la pantalla (dedo, pluma stylus, mouse)
  • Eje: tipo de datos (coordenadas x e y, presión, inclinación, orientación y desplazamiento [distancia])

Acciones

Para implementar la compatibilidad con la pluma stylus, debes comprender qué acción realiza el usuario.

MotionEvent proporciona una amplia variedad de constantes ACTION que definen eventos de movimiento. Algunas de las acciones más importantes de la pluma stylus son las siguientes:

Acción Descripción
ACTION_DOWN
ACTION_POINTER_DOWN
El puntero entró en contacto con la pantalla.
ACTION_MOVE El puntero se está moviendo en la pantalla.
ACTION_UP
ACTION_POINTER_UP
El puntero ya no está en contacto con la pantalla
ACTION_CANCEL Cuándo se debe cancelar el conjunto de movimientos anterior o actual.

Tu app puede realizar tareas como iniciar un trazo nuevo cuando se produce ACTION_DOWN, dibujar el trazo con ACTION_MOVE, y finalizarlo cuando se activa ACTION_UP.

El conjunto de acciones MotionEvent de ACTION_DOWN a ACTION_UP para un puntero determinado se denomina conjunto de movimientos.

Punteros

La mayoría de las pantallas son multitáctiles: el sistema asigna un puntero para cada dedo, pluma stylus, mouse u otro objeto del estilo que interactúe con la pantalla. El índice del puntero te permite obtener información sobre los ejes de un puntero específico, como la posición del primer dedo que toca la pantalla o el segundo.

Los índices del puntero van desde cero hasta la cantidad de punteros que muestra MotionEvent#pointerCount() menos 1.

Se puede acceder a los valores de los ejes de los punteros con el método getAxisValue(axis, pointerIndex). Cuando se omite el índice del puntero, el sistema muestra el valor del primer puntero, puntero cero (0).

Los objetos MotionEvent contienen información sobre el tipo de puntero que se usa. Para obtener el tipo de puntero, debes iterar a través de los índices del puntero y llamar al método getToolType(pointerIndex).

Para obtener más información sobre los punteros, consulta Cómo controlar gestos multitáctiles.

Entradas de la pluma stylus

Puedes filtrar las entradas de la pluma stylus con TOOL_TYPE_STYLUS:

Kotlin

val isStylus = TOOL_TYPE_STYLUS == event.getToolType(pointerIndex)

Java

boolean isStylus = TOOL_TYPE_STYLUS == event.getToolType(pointerIndex);

La pluma stylus también puede informar si se usa como borrador con TOOL_TYPE_ERASER:

Kotlin

val isEraser = TOOL_TYPE_ERASER == event.getToolType(pointerIndex)

Java

boolean isEraser = TOOL_TYPE_ERASER == event.getToolType(pointerIndex);

Datos del eje de la pluma stylus

ACTION_DOWN y ACTION_MOVE proporcionan datos del eje sobre la pluma stylus, es decir, las coordenadas x e y, la presión, la orientación, la inclinación y el desplazamiento.

Para habilitar el acceso a estos datos, la API de MotionEvent proporciona getAxisValue(int), donde el parámetro es cualquiera de los siguientes identificadores del eje:

Axis Valor que se muestra de getAxisValue()
AXIS_X Coordenada X de un evento de movimiento.
AXIS_Y Coordenada Y de un evento de movimiento.
AXIS_PRESSURE Para una pantalla táctil o un panel táctil, la presión que se aplica con un dedo, una pluma stylus u otro puntero. Para un mouse o una bola de seguimiento, 1 si se presiona el botón principal, 0 de lo contrario.
AXIS_ORIENTATION En el caso de una pantalla táctil o un panel táctil, la orientación de un dedo, una pluma stylus u otro puntero en relación con el plano vertical del dispositivo.
AXIS_TILT El ángulo de inclinación de la pluma stylus en radianes.
AXIS_DISTANCE La distancia de la pluma stylus a la pantalla.

Por ejemplo, MotionEvent.getAxisValue(AXIS_X) muestra la coordenada x para el primer puntero.

Consulta también Cómo controlar gestos multitáctiles.

Posición

Puedes recuperar las coordenadas x e y de un puntero con las siguientes llamadas:

Dibujo de una pluma stylus en la pantalla con coordenadas x e y.
Figura 1: Coordenadas de pantalla x e y de un puntero de pluma stylus.

Presión

Puedes conocer la presión del puntero con las siguientes llamadas:

getAxisValue(AXIS_PRESSURE) o getPressure() para el primer puntero.

El valor de presión de las pantallas táctiles o los paneles táctiles es un valor entre 0 (sin presión) y 1, pero se pueden mostrar valores más altos según la calibración de la pantalla.

Trazo de la pluma stylus que representa una continuidad de presión baja a alta. El trazo es angosto y débil a la izquierda, lo que indica presión baja. El trazo se hace más ancho y más oscuro de izquierda a derecha hasta que queda más ancho y más oscuro en el extremo derecho, lo que indica presión más alta.
Figura 2: Representación de presión: presión baja a la izquierda, presión alta a la derecha.

Orientación

La orientación indica hacia qué dirección apunta la pluma stylus.

La orientación del puntero se puede recuperar con getAxisValue(AXIS_ORIENTATION) o getOrientation() (para el primer puntero).

En una pluma stylus, la orientación se muestra como un valor de radianes entre 0 y pi (π) en sentido horario, o entre 0 y -pi en sentido antihorario.

La orientación te permite implementar un pincel realista. Por ejemplo, si la pluma stylus representa un pincel plano, su ancho depende de la orientación de la pluma stylus.

Figura 3: Pluma stylus que apunta hacia la izquierda alrededor de menos 0.57 radianes.

Inclinación

Esta función mide la inclinación de la pluma stylus en relación con la pantalla.

La inclinación muestra el ángulo positivo de la pluma stylus en radianes, donde cero es perpendicular a la pantalla y π/2 es plano respecto de la superficie.

El ángulo de inclinación se puede recuperar con getAxisValue(AXIS_TILT) (sin combinación de teclas para el primer puntero).

La inclinación se puede usar para reproducir herramientas realistas de la forma más precisa posible, como imitar el sombreado con un lápiz inclinado.

Pluma stylus inclinada a aproximadamente 40 grados respecto de la superficie de la pantalla.
Figura 4: Pluma stylus inclinada a aproximadamente 0.785 radianes o 45 grados respecto de la perpendicular.

Colocar el cursor sobre un elemento

La distancia de la pluma stylus a la pantalla se puede obtener con getAxisValue(AXIS_DISTANCE). El método muestra un valor de 0.0 (contacto con la pantalla) a valores más altos a medida que la pluma stylus se aleja de la pantalla. La distancia de desplazamiento entre la pantalla y la punta de la pluma stylus depende del fabricante de la pantalla y de la pluma stylus. Debido a que las implementaciones pueden variar, no dependas de valores precisos para funcionalidades críticas de la app.

El desplazamiento de la pluma stylus se puede utilizar para obtener una vista previa del tamaño del pincel o indicar que se seleccionará un botón.

Figura 5: Pluma stylus sobre una pantalla. La app reacciona aunque la pluma stylus no toque la superficie de la pantalla.

Nota: Compose ofrece un conjunto de elementos modificadores para cambiar el estado de los elementos de la IU.

  • hoverable: Configura este componente para que se pueda colocar el cursor sobre él con los eventos de entrada y salida del puntero.
  • indication: Dibuja efectos visuales para este componente cuando se producen interacciones.

Rechazo de la palma, navegación y entradas no deseadas

A veces, las pantallas multitáctiles pueden registrar toques no deseados, por ejemplo, cuando un usuario apoya naturalmente la mano en la pantalla para descansar mientras escribe. El rechazo de la palma es un mecanismo que detecta este comportamiento y te notifica que se debe cancelar el último conjunto de MotionEvent.

Por lo tanto, debes mantener un historial de entradas del usuario para que se puedan quitar los toques no deseados de la pantalla y se puedan volver a renderizar las entradas del usuario verdaderas.

ACTION_CANCEL y FLAG_CANCELED

ACTION_CANCEL y FLAG_CANCELED están diseñados para informarte que se debe cancelar el conjunto anterior de MotionEvent del último ACTION_DOWN. Por ejemplo, puedes deshacer el último trazo que un puntero determinado hace en una app de dibujo.

ACTION_CANCEL

Se agregó en Android 1.0 (nivel de API 1)

ACTION_CANCEL indica que se debe cancelar el conjunto anterior de eventos de movimiento.

ACTION_CANCEL se activa cuando se detecta alguna de las siguientes opciones:

  • Gestos de navegación
  • Rechazo de la palma

Cuando se activa ACTION_CANCEL, debes identificar el puntero activo con getPointerId(getActionIndex()). Luego, quita el trazo creado con ese puntero del historial de entradas y vuelve a renderizar la escena.

FLAG_CANCELED

Se agregó en Android 13 (nivel de API 33)

FLAG_CANCELED indica que el movimiento del puntero hacia arriba fue un toque no intencional del usuario. Por lo general, la marca se establece cuando el usuario toca la pantalla accidentalmente; por ejemplo, cuando agarra el dispositivo o coloca la palma de la mano sobre la pantalla.

Puedes acceder al valor de la marca de la siguiente manera:

Kotlin

val cancel = (event.flags and FLAG_CANCELED) == FLAG_CANCELED

Java

boolean cancel = (event.getFlags() & FLAG_CANCELED) == FLAG_CANCELED;

Si la marca está establecida, debes deshacer el último conjunto de MotionEvent del último ACTION_DOWN de este puntero.

Al igual que ACTION_CANCEL, el puntero se puede encontrar con getPointerId(actionIndex).

Figura 6: El trazo de la pluma stylus y el toque de la palma crean conjuntos de MotionEvent. Se canceló el toque de la palma y se volvió a renderizar la pantalla.

Gestos de pantalla completa, de borde a borde y de navegación

Si una app está en pantalla completa y tiene elementos prácticos cerca del borde, como el lienzo de una app para dibujar o tomar notas, deslizar el dedo desde la parte inferior de la pantalla para mostrar la navegación o mover la app al segundo plano puede provocar una acción no deseada en el lienzo.

Figura 7: Gesto de deslizar el dedo para mover una app al segundo plano.

Para evitar que los gestos activen toques no deseados en tu app, puedes aprovechar las inserciones y ACTION_CANCEL.

Consulta también Rechazo de la palma, navegación y entradas no deseadas más arriba.

Usa el método setSystemBarsBehavior() y BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE de WindowInsetsController para evitar que los gestos de navegación causen eventos táctiles no deseados:

Kotlin

// Configure the behavior of the hidden system bars.
windowInsetsController.systemBarsBehavior =
    WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE

Java

// Configure the behavior of the hidden system bars.
windowInsetsController.setSystemBarsBehavior(
    WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
);

Para obtener más información sobre la administración de inserciones y gestos, consulta:

Latencia baja

La latencia es el tiempo que requieren el hardware, el sistema y la aplicación para procesar y renderizar la entrada del usuario.

Latencia = hardware y procesamiento de entrada del SO + procesamiento de la app + composición del sistema + renderización de hardware

La latencia hace que el trazo renderizado se retrase con respecto a la posición de la pluma stylus. El espacio entre el trazo renderizado y la posición de la pluma stylus representa la latencia.
Figura 8: La latencia hace que el trazo renderizado se retrase con respecto a la posición de la pluma stylus.

Fuente de latencia

  • Registro de la pluma stylus con pantalla táctil (hardware): Indica una conexión inalámbrica inicial cuando la pluma stylus y el SO se comunican para registrarse y sincronizarse.
  • Tasa de muestreo táctil (hardware): Es la cantidad de veces por segundo que una pantalla táctil comprueba si un puntero toca la superficie, entre 60 y 1000 Hz.
  • Procesamiento de entrada (app): Aplicación de color, efectos gráficos y transformación de las entradas del usuario.
  • Renderización gráfica (SO + hardware): Cambio de búfer, procesamiento de hardware.

Gráficos de baja latencia

La biblioteca de gráficos de baja latencia de Jetpack reduce el tiempo de procesamiento entre la entrada del usuario y la renderización en pantalla.

La biblioteca reduce el tiempo de procesamiento, ya que evita la renderización de varios búferes y aprovecha una técnica de renderización de búfer frontal, lo que implica escribir directamente en la pantalla.

Renderización de búfer frontal

El búfer frontal es la memoria que usa la pantalla para la renderización. Es la forma más cercana en que las apps pueden dibujar directamente en la pantalla. La biblioteca de baja latencia permite que las apps se rendericen directamente en el búfer frontal. Esto mejora el rendimiento, ya que evita el intercambio de búfer, que puede ocurrir en la renderización habitual de varios búferes o en la renderización de doble búfer (el caso más común).

La app escribe en el búfer de pantalla y lee desde él.
Figura 9: Renderización del búfer frontal.
La app escribe en un búfer múltiple, que se intercala con el búfer de pantalla. La app lee del búfer de pantalla.
Figura 10: Renderización de búfer múltiple.

Si bien la renderización del búfer frontal es una excelente técnica para renderizar un área pequeña de la pantalla, no está diseñada para usarse en la actualización de toda la pantalla. Con la renderización del búfer frontal, la app renderiza contenido en un búfer desde el que se lee la pantalla. Como resultado, existe la posibilidad de renderizar artefactos o seccionamientos (consulta a continuación).

La biblioteca de baja latencia está disponible desde Android 10 (nivel de API 29) y versiones posteriores, y en dispositivos ChromeOS con Android 10 (nivel de API 29) y versiones posteriores.

Dependencias

La biblioteca de baja latencia proporciona los componentes para la implementación de la renderización en el búfer frontal. La biblioteca se agrega como una dependencia en el archivo build.gradle del módulo de la app:

dependencies {
    implementation "androidx.graphics:graphics-core:1.0.0-alpha03"
}

Devoluciones de llamada de GLFrontBufferRenderer

La biblioteca de baja latencia incluye la interfaz GLFrontBufferRenderer.Callback, que define los siguientes métodos:

La biblioteca de baja latencia no se expresa sobre el tipo de datos que usas con GLFrontBufferRenderer.

Sin embargo, la biblioteca procesa los datos como un flujo de cientos de datos. Por lo tanto, diseña tus datos para optimizar el uso y la asignación de la memoria.

Devoluciones de llamada

Para habilitar las devoluciones de llamada de renderización, implementa GLFrontBufferedRenderer.Callback y anula onDrawFrontBufferedLayer() y onDrawDoubleBufferedLayer(). GLFrontBufferedRenderer usa las devoluciones de llamada para renderizar tus datos de la manera más optimizada posible.

Kotlin

val callback = object: GLFrontBufferedRenderer.Callback<DATA_TYPE> {

   override fun onDrawFrontBufferedLayer(
       eglManager: EGLManager,
       bufferInfo: BufferInfo,
       transform: FloatArray,
       param: DATA_TYPE
   ) {
       // OpenGL for front buffer, short, affecting small area of the screen.
   }

   override fun onDrawMultiDoubleBufferedLayer(
       eglManager: EGLManager,
       bufferInfo: BufferInfo,
       transform: FloatArray,
       params: Collection<DATA_TYPE>
   ) {
       // OpenGL full scene rendering.
   }
}

Java

GLFrontBufferedRenderer.Callback<DATA_TYPE> callbacks =
    new GLFrontBufferedRenderer.Callback<DATA_TYPE>() {
        @Override
        public void onDrawFrontBufferedLayer(@NonNull EGLManager eglManager,
            @NonNull BufferInfo bufferInfo,
            @NonNull float[] transform,
            DATA_TYPE data_type) {
                // OpenGL for front buffer, short, affecting small area of the screen.
        }

    @Override
    public void onDrawDoubleBufferedLayer(@NonNull EGLManager eglManager,
        @NonNull BufferInfo bufferInfo,
        @NonNull float[] transform,
        @NonNull Collection<? extends DATA_TYPE> collection) {
            // OpenGL full scene rendering.
    }
};
Cómo declarar una instancia de GLFrontBufferedRenderer

Para preparar el GLFrontBufferedRenderer, proporciona la SurfaceView y las devoluciones de llamada que creaste anteriormente. GLFrontBufferedRenderer optimiza la renderización en el búfer frontal y doble mediante las devoluciones de llamada:

Kotlin

var glFrontBufferRenderer = GLFrontBufferedRenderer<DATA_TYPE>(surfaceView, callbacks)

Java

GLFrontBufferedRenderer<DATA_TYPE> glFrontBufferRenderer =
    new GLFrontBufferedRenderer<DATA_TYPE>(surfaceView, callbacks);
Renderización

La renderización del búfer frontal comienza cuando llamas al método renderFrontBufferedLayer(), que activa la devolución de llamada de onDrawFrontBufferedLayer().

La renderización del búfer doble se reanuda cuando llamas a la función commit(), que activa la devolución de llamada de onDrawMultiDoubleBufferedLayer().

En el siguiente ejemplo, el proceso se renderiza en el búfer frontal (renderización rápida) cuando el usuario comienza a dibujar en la pantalla (ACTION_DOWN) y mueve el puntero (ACTION_MOVE). El proceso se renderiza en el búfer doble cuando el puntero sale de la superficie de la pantalla (ACTION_UP).

Puedes usar requestUnbufferedDispatch() para solicitar que el sistema de entrada no agrupe los eventos de movimiento, sino que los envíe en cuanto estén disponibles:

Kotlin

when (motionEvent.action) {
   MotionEvent.ACTION_DOWN -> {
       // Deliver input events as soon as they arrive.
       view.requestUnbufferedDispatch(motionEvent)
       // Pointer is in contact with the screen.
       glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE)
   }
   MotionEvent.ACTION_MOVE -> {
       // Pointer is moving.
       glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE)
   }
   MotionEvent.ACTION_UP -> {
       // Pointer is not in contact in the screen.
       glFrontBufferRenderer.commit()
   }
   MotionEvent.CANCEL -> {
       // Cancel front buffer; remove last motion set from the screen.
       glFrontBufferRenderer.cancel()
   }
}

Java

switch (motionEvent.getAction()) {
   case MotionEvent.ACTION_DOWN: {
       // Deliver input events as soon as they arrive.
       surfaceView.requestUnbufferedDispatch(motionEvent);

       // Pointer is in contact with the screen.
       glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE);
   }
   break;
   case MotionEvent.ACTION_MOVE: {
       // Pointer is moving.
       glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE);
   }
   break;
   case MotionEvent.ACTION_UP: {
       // Pointer is not in contact in the screen.
       glFrontBufferRenderer.commit();
   }
   break;
   case MotionEvent.ACTION_CANCEL: {
       // Cancel front buffer; remove last motion set from the screen.
       glFrontBufferRenderer.cancel();
   }
   break;
}

Sugerencias y precauciones para la renderización

Sugerencia

Puede utilizarse en pequeñas partes de la pantalla, escritura a mano, dibujo y esbozo.

Qué no puedes hacer

Actualización en pantalla completa, desplazamiento lateral y zoom. Puede provocar seccionamientos.

Seccionamientos

Los seccionamientos se producen cuando la pantalla se actualiza mientras se modifica el búfer de pantalla al mismo tiempo. Una parte de la pantalla muestra datos nuevos, mientras que la otra muestra datos antiguos.

Las partes inferior y superior de la imagen de Android no están alineadas debido al seccionamiento cuando se actualiza la pantalla.
Figura 11: El seccionamiento se actualiza de la parte superior a la inferior.

Predicción del movimiento

La biblioteca de Jetpack de predicción del movimiento reduce la latencia percibida gracias a que estima la ruta del trazo del usuario y proporciona puntos artificiales y temporales al procesador.

La biblioteca de predicción del movimiento obtiene entradas reales del usuario como objetos MotionEvent. Los objetos contienen información sobre las coordenadas x e y, la presión y el tiempo, que el predictor de movimientos utiliza para predecir objetos MotionEvent futuros.

Los objetos MotionEvent pronosticados son solo estimaciones. Los eventos pronosticados pueden reducir la latencia percibida, pero los datos pronosticados deben reemplazarse por los datos reales de MotionEvent una vez que se reciben.

La biblioteca de predicción del movimiento está disponible en Android 4.4 (nivel de API 19) y versiones posteriores, y en dispositivos ChromeOS con Android 9 (nivel de API 28) y versiones posteriores.

La latencia hace que el trazo renderizado se retrase con respecto a la posición de la pluma stylus. El espacio entre el trazo y la pluma stylus se llena con puntos de predicción. La brecha restante es la latencia percibida.
Figura 12: Se redujo la latencia mediante la predicción del movimiento.

Dependencias

La biblioteca de predicción del movimiento proporciona la implementación de la predicción. La biblioteca se agrega como una dependencia en el archivo build.gradle del módulo de la app:

dependencies {
    implementation "androidx.input:input-motionprediction:1.0.0-beta01"
}

Implementación

La biblioteca de predicción del movimiento incluye la interfaz MotionEventPredictor, que define los siguientes métodos:

  • record(): Almacena objetos MotionEvent como un registro de las acciones del usuario.
  • predict(): Muestra un MotionEvent pronosticado.
Declara una instancia de MotionEventPredictor.

Kotlin

var motionEventPredictor = MotionEventPredictor.newInstance(view)

Java

MotionEventPredictor motionEventPredictor = MotionEventPredictor.newInstance(surfaceView);
Cómo alimentar el predictor con datos

Kotlin

motionEventPredictor.record(motionEvent)

Java

motionEventPredictor.record(motionEvent);
Predecir

Kotlin

when (motionEvent.action) {
   MotionEvent.ACTION_MOVE -> {
       val predictedMotionEvent = motionEventPredictor?.predict()
       if(predictedMotionEvent != null) {
            // use predicted MotionEvent to inject a new artificial point
       }
   }
}

Java

switch (motionEvent.getAction()) {
   case MotionEvent.ACTION_MOVE: {
       MotionEvent predictedMotionEvent = motionEventPredictor.predict();
       if(predictedMotionEvent != null) {
           // use predicted MotionEvent to inject a new artificial point
       }
   }
   break;
}

Sugerencias y precauciones para la predicción del movimiento

Sugerencia

Quita los puntos de predicción cuando se agregue un nuevo punto pronosticado.

Qué no debes hacer

No uses puntos de predicción para la renderización final.

Apps para tomar notas

ChromeOS permite que tu app declare algunas acciones para tomar notas.

Si quieres registrar una app para tomar notas en ChromeOS, consulta Compatibilidad con entradas.

Si quieres registrar una app para tomar notas en Android, consulta Cómo crear una app para tomar notas.

Android 14 (nivel de API 34) introdujo el intent ACTION_CREATE_NOTE, que permite que tu app inicie una actividad para tomar notas en la pantalla de bloqueo.

Reconocimiento de tinta digital con ML Kit

Con el reconocimiento de tinta digital del ML Kit, tu app puede reconocer texto escrito a mano en una superficie digital en cientos de idiomas. También puedes clasificar bocetos.

ML Kit proporciona la clase Ink.Stroke.Builder para crear objetos Ink que los modelos de aprendizaje automático pueden procesar para convertir el texto escrito a mano en texto.

Además del reconocimiento del texto escrito a mano, el modelo puede reconocer gestos, como borrar y encerrar en un círculo.

Consulta Reconocimiento de tinta digital para obtener más información.

Recursos adicionales

Guías para desarrolladores

Codelabs