Supporto delle funzionalità avanzate dello stilo

Lo stilo consente agli utenti di interagire comodamente con le app e con la massima precisione per prendere appunti, disegnare, lavorare con app di produttività, rilassarsi e divertirsi con giochi e app di intrattenimento.

Android e ChromeOS offrono una varietà di API per creare un'esperienza eccezionale con lo stilo nelle app. La classe MotionEvent fornisce informazioni sull'interazione dell'utente con lo schermo, ad esempio la pressione dello stilo, l'orientamento, l'inclinazione, il passaggio del mouse e il rilevamento del palmo. Le librerie di previsioni di movimento e di grafica a bassa latenza migliorano il rendering sullo schermo dello stilo per offrire un'esperienza naturale simile a carta e penna.

MotionEvent

La classe MotionEvent rappresenta le interazioni di input dell'utente, ad esempio la posizione e il movimento dei puntatori touch sullo schermo. Per l'input con lo stilo, MotionEvent espone anche i dati relativi a pressione, orientamento, inclinazione e passaggio del mouse.

Dati sugli eventi

Per accedere ai dati MotionEvent nelle app basate sulle visualizzazioni, configura un onTouchListener:

Kotlin

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

Java

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

Il listener riceve MotionEvent oggetti dal sistema, quindi la tua app può elaborarli.

Un oggetto MotionEvent fornisce dati relativi ai seguenti aspetti di un evento UI:

  • Azioni: interazione fisica con il dispositivo: tocco dello schermo, spostamento di un puntatore sulla superficie dello schermo, passaggio del puntatore del mouse sulla superficie dello schermo
  • Puntatori: identificatori degli oggetti che interagiscono con lo schermo (dita, stilo, mouse).
  • Asse: tipo di dati, coordinate x e y, pressione, inclinazione, orientamento e passaggio del mouse (distanza)

Azioni

Per implementare il supporto dello stilo, devi capire quale azione sta eseguendo l'utente.

MotionEvent fornisce un'ampia varietà di costanti ACTION che definiscono gli eventi di movimento. Le azioni più importanti per lo stilo includono:

Azione Descrizione
ACTION_DOWN
ACTION_POINTER_DOWN
Il puntatore ha stabilito un contatto con lo schermo.
ACTION_MOVE Il puntatore si muove sullo schermo.
ACTION_UP
ACTION_POINTER_UP
Il puntatore non è più in contatto con lo schermo
AZIONE_ANNULLA Indica quando il set di movimento precedente o corrente deve essere annullato.

La tua app può eseguire attività come iniziare un nuovo tratto quando si verifica ACTION_DOWN, disegnare il tratto con ACTION_MOVE, e finire il tratto quando viene attivato ACTION_UP.

L'insieme di MotionEvent azioni da ACTION_DOWN a ACTION_UP per un determinato puntatore viene chiamato insieme di movimento.

Puntatori

La maggior parte delle schermate è multi-touch: il sistema assegna un puntatore a ogni dito, stilo, mouse o altro oggetto di puntamento che interagisce con lo schermo. L'indice del puntatore ti consente di ottenere le informazioni dell'asse di un puntatore specifico, ad esempio la posizione del primo dito che tocca lo schermo o del secondo.

Gli indici puntatori sono compresi tra zero e il numero di puntatori restituiti da MotionEvent#pointerCount() meno 1.

Puoi accedere ai valori dell'asse dei puntatori con il metodo getAxisValue(axis, pointerIndex). Quando l'indice del puntatore viene omesso, il sistema restituisce il valore del primo puntatore, ovvero il puntatore zero (0).

Gli oggetti MotionEvent contengono informazioni sul tipo di puntatore in uso. Per ottenere il tipo di puntatore, esegui l'iterazione degli indici di puntatore e chiama il metodo getToolType(pointerIndex).

Per scoprire di più sui puntatori, consulta l'articolo Gestire i gesti multi-touch.

Input stilo

Puoi filtrare gli input dello stilo con TOOL_TYPE_STYLUS:

Kotlin

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

Java

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

Lo stilo può anche segnalare il suo utilizzo come gomma con TOOL_TYPE_ERASER:

Kotlin

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

Java

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

Dati dell'asse dello stilo

ACTION_DOWN e ACTION_MOVE forniscono dati degli assi relativi allo stilo, ovvero le coordinate X e Y, la pressione, l'orientamento, l'inclinazione e il passaggio del mouse.

Per consentire l'accesso a questi dati, l'API MotionEvent fornisce getAxisValue(int), in cui il parametro è uno dei seguenti identificatori di asse:

Axis Valore restituito di getAxisValue()
AXIS_X Coordinata X di un evento di movimento.
AXIS_Y Coordinata Y di un evento di movimento.
AXIS_PRESSURE Per un touchscreen o un touchpad, la pressione applicata da un dito, uno stilo o un altro puntatore. Per un mouse o una trackball, 1 se si preme il pulsante principale, 0 altrimenti.
AXIS_ORIENTATION Per un touchscreen o un touchpad, l'orientamento di un dito, stilo o altro puntatore rispetto al piano verticale del dispositivo.
AXIS_TILT L'angolo di inclinazione dello stilo espresso in radianti.
AXIS_DISTANCE La distanza dello stilo dallo schermo.

Ad esempio, MotionEvent.getAxisValue(AXIS_X) restituisce la coordinata x per il primo puntatore.

Vedi anche Gestire i gesti multi-touch.

Posizione

Puoi recuperare le coordinate x e y di un puntatore con le seguenti chiamate:

Disegno sullo schermo con lo stilo con le coordinate X e Y mappate.
Figura 1. Coordinate sullo schermo X e Y del puntatore di uno stilo.

Pressione

Puoi recuperare la pressione del puntatore con le seguenti chiamate:

getAxisValue(AXIS_PRESSURE) o getPressure() per il primo puntatore.

Il valore di pressione per touchscreen o touchpad è un valore compreso tra 0 (nessuna pressione) e 1, ma possono essere restituiti valori più elevati in base alla calibrazione dello schermo.

Tratto dello stilo che rappresenta un continuum di pressione da bassa ad alta. Il tratto è stretto e debole a sinistra, a indicare una bassa pressione. Il tratto diventa più ampio e più scuro da sinistra a destra fino a raggiungere la larghezza massima e il più scuro all'estrema destra, a indicare la pressione massima.
Figura 2. Rappresentazione della pressione: bassa pressione a sinistra, alta pressione a destra.

Orientamento

L'orientamento indica la direzione in cui punta lo stilo.

L'orientamento del puntatore può essere recuperato utilizzando getAxisValue(AXIS_ORIENTATION) o getOrientation() (per il primo puntatore).

Per uno stilo, l'orientamento viene restituito sotto forma di radianti compreso tra 0 e pi greco (π) in senso orario o tra 0 e -pi in senso antiorario.

L'orientamento ti consente di applicare un pennello reale. Ad esempio, se lo stilo rappresenta un pennello piatto, la larghezza del pennello piatto dipende dall'orientamento dello stilo.

Figura 3. Stilo rivolto a sinistra di circa 0,57 radianti.

Inclinazione

L'inclinazione misura l'inclinazione dello stilo rispetto allo schermo.

L'inclinazione restituisce l'angolo positivo dello stilo espresso in radianti, dove lo zero è perpendicolare allo schermo e π/2 è piatto sulla superficie.

L'angolo di inclinazione può essere recuperato utilizzando getAxisValue(AXIS_TILT) (nessuna scorciatoia per il primo puntatore).

L'inclinazione può essere utilizzata per riprodurre il più vicino possibile strumenti reali, come simulare l'ombreggiatura con una matita inclinata.

Stilo inclinato di circa 40 gradi dalla superficie dello schermo.
Figura 4. Stilo inclinato di circa 0, 785 radianti o di 45 gradi dalla perpendicolare.

Passaggio

Puoi ottenere la distanza dello stilo dallo schermo usando getAxisValue(AXIS_DISTANCE). Il metodo restituisce un valore compreso tra 0,0 (contatto con lo schermo) e valori più alti quando lo stilo si allontana dallo schermo. La distanza del passaggio del mouse tra lo schermo e la punta (punto) dello stilo dipende dal produttore dello schermo e dello stilo. Poiché le implementazioni possono variare, non fare affidamento su valori precisi per le funzionalità fondamentali per l'app.

È possibile usare il passaggio del mouse sullo stilo per visualizzare l'anteprima delle dimensioni del pennello o indicare che verrà selezionato un pulsante.

Figura 5. Stilo che passa sopra uno schermo. L'app reagisce anche se lo stilo non tocca la superficie dello schermo.

Nota:Compose offre un insieme di elementi di modifica per cambiare lo stato degli elementi dell'interfaccia utente:

  • hoverable: configura il componente in modo che sia possibile passare il mouse sopra gli eventi di entrata e uscita del puntatore.
  • indication: disegna effetti visivi per questo componente quando si verificano interazioni.

Rifiuto del palmo, navigazione e input indesiderati

A volte gli schermi multi-touch possono registrare i tocchi indesiderati, ad esempio quando un utente appoggia naturalmente la mano sullo schermo come supporto durante la scrittura a mano libera. Il rifiuto del palmo è un meccanismo che rileva questo comportamento e ti avvisa che l'ultima serie di MotionEvent deve essere annullata.

Di conseguenza, devi conservare una cronologia degli input utente in modo che i tocchi indesiderati possano essere rimossi dallo schermo e che gli input legittimi possano essere nuovamente visualizzati.

ACTION_CANCEL e FLAG_CANCELED

ACTION_CANCEL e FLAG_CANCELED sono entrambi progettati per informarti che il precedente set MotionEvent deve essere annullato dall'ultimo ACTION_DOWN, in modo da poter, ad esempio, annullare l'ultimo tratto di un'app di disegno per un determinato puntatore.

AZIONE_ANNULLA

Aggiunto in Android 1.0 (livello API 1)

ACTION_CANCEL indica che l'insieme di eventi di movimento precedente deve essere annullato.

ACTION_CANCEL viene attivato quando viene rilevata una delle seguenti caratteristiche:

  • Gesti di navigazione
  • Rifiuto del palmo

Quando viene attivato ACTION_CANCEL, devi identificare il puntatore attivo con getPointerId(getActionIndex()). Quindi rimuovi il tratto creato con il puntatore dalla cronologia di input ed esegui di nuovo il rendering della scena.

SEGNALAZIONE_ANNULLATA

Aggiunta in Android 13 (livello API 33)

FLAG_CANCELED indica che il cursore verso l'alto è stato causato da un tocco non intenzionale dell'utente. La bandiera viene generalmente impostata quando l'utente tocca accidentalmente lo schermo, ad esempio afferrando il dispositivo o posizionando il palmo della mano sullo schermo.

Per accedere al valore del flag:

Kotlin

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

Java

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

Se il flag è impostato, devi annullare l'ultima serie di MotionEvent, a partire dall'ultimo ACTION_DOWN di questo puntatore.

Come ACTION_CANCEL, il puntatore si trova con getPointerId(actionIndex).

Figura 6. Il tratto con lo stilo e il tocco del palmo creano MotionEvent set. Il tocco con il palmo viene annullato e il display viene sottoposto di nuovo al rendering.

Gesti a schermo intero, da bordo a bordo e di navigazione

Se un'app è a schermo intero e ha elementi interattivi vicino al bordo, ad esempio la tela di un'app per disegnare o per scrivere note, scorrere dalla parte inferiore dello schermo per visualizzare la navigazione o spostare l'app sullo sfondo potrebbe causare un tocco indesiderato sulla tela.

Figura 7. Gesto di scorrimento per spostare un'app sullo sfondo.

Per evitare che i gesti attivino tocchi indesiderati nella tua app, puoi utilizzare gli inset e gli ACTION_CANCEL.

Consulta anche la sezione Rifiuto, navigazione e input indesiderati di Palm qui sopra.

Usa il metodo setSystemBarsBehavior() e BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE di WindowInsetsController per evitare che i gesti di navigazione provochino eventi di tocco indesiderati:

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
);

Per scoprire di più sulla gestione di riquadri e gesti, vedi:

Bassa latenza

La latenza è il tempo necessario all'hardware, al sistema e all'applicazione per elaborare e visualizzare l'input utente.

Latenza = elaborazione dell'input di hardware e sistema operativo + elaborazione delle app + composizione del sistema + rendering dell'hardware

La latenza causa un ritardo nella posizione dello stilo per il tratto sottoposto a rendering. Lo spazio tra il tratto visualizzato e la posizione dello stilo rappresenta la latenza.
Figura 8. La latenza causa un ritardo nella posizione dello stilo per il tratto sottoposto a rendering.

Origine della latenza

  • Registrazione dello stilo con touchscreen (hardware): connessione wireless iniziale quando lo stilo e il sistema operativo comunicano per essere registrati e sincronizzati.
  • Frequenza di campionamento al tocco (hardware): il numero di volte al secondo un touchscreen verifica se un puntatore tocca la superficie, compreso tra 60 e 1000 Hz.
  • Elaborazione dell'input (app): applicazione di colore, effetti grafici e trasformazione all'input utente.
  • Rendering grafico (sistema operativo e hardware): scambio del buffer, elaborazione dell'hardware.

Grafica a bassa latenza

La libreria grafica a bassa latenza Jetpack riduce il tempo di elaborazione tra l'input utente e il rendering sullo schermo.

La libreria riduce i tempi di elaborazione evitando il rendering multi-buffer e utilizzando una tecnica di rendering con buffer frontale, che significa scrivere direttamente sullo schermo.

Rendering buffer anteriore

Il buffer anteriore è la memoria utilizzata dallo schermo per il rendering. È l'app più vicina che può disegnare direttamente sullo schermo. La libreria a bassa latenza consente alle app di eseguire il rendering direttamente nel buffer anteriore. Ciò migliora le prestazioni impedendo lo scambio del buffer, che può accadere per il normale rendering multi-buffer o con il rendering a doppio buffer (il caso più comune).

L'app scrive nel buffer dello schermo e legge dal buffer.
Figura 9. Rendering del buffer anteriore.
L'app scrive sul multi-buffer, che viene scambiato con il buffer dello schermo. L'app legge dal buffer dello schermo.
Figura 10. Rendering multi-buffer.

Il rendering buffer frontale è un'ottima tecnica per eseguire il rendering di una piccola area dello schermo, ma non è progettato per essere utilizzato per aggiornare l'intero schermo. Con il rendering con buffer frontale, l'app esegue il rendering dei contenuti in un buffer di lettura dal display. Di conseguenza, esiste la possibilità di visualizzare gli artefatti o di strapparsi (vedi sotto).

La libreria a bassa latenza è disponibile su Android 10 (livello API 29) e versioni successive e sui dispositivi ChromeOS con Android 10 (livello API 29) e versioni successive.

Dipendenze

La libreria a bassa latenza fornisce i componenti per l'implementazione del rendering del buffer frontale. La libreria viene aggiunta come dipendenza nel file build.gradle del modulo dell'app:

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

Callback di GLFrontBufferRenderer

La libreria a bassa latenza include l'interfaccia GLFrontBufferRenderer.Callback, che definisce i seguenti metodi:

La libreria a bassa latenza non si basa sul tipo di dati che utilizzi con GLFrontBufferRenderer.

Tuttavia, la libreria elabora i dati come un flusso di centinaia di punti dati, quindi progetta i tuoi dati per ottimizzare l'utilizzo e l'allocazione della memoria.

Richiamate

Per attivare i callback di rendering, implementa GLFrontBufferedRenderer.Callback ed esegui l'override di onDrawFrontBufferedLayer() e onDrawDoubleBufferedLayer(). GLFrontBufferedRenderer utilizza i callback per visualizzare i tuoi dati nel modo più ottimizzato possibile.

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.
    }
};
Dichiara un'istanza di GLFrontBufferedRenderer

Prepara il GLFrontBufferedRenderer fornendo i SurfaceView e i callback che hai creato in precedenza. GLFrontBufferedRenderer ottimizza il rendering in primo piano ed esegue il doppio buffer utilizzando i callback:

Kotlin

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

Java

GLFrontBufferedRenderer<DATA_TYPE> glFrontBufferRenderer =
    new GLFrontBufferedRenderer<DATA_TYPE>(surfaceView, callbacks);
Rendering

Il rendering del buffer anteriore inizia quando chiami il metodo renderFrontBufferedLayer(), che attiva il callback onDrawFrontBufferedLayer().

Il rendering del doppio buffer riprende quando chiami la funzione commit(), che attiva il callback onDrawMultiDoubleBufferedLayer().

Nell'esempio che segue, il processo esegue il rendering nel buffer anteriore (rendering veloce) quando l'utente inizia a disegnare sullo schermo (ACTION_DOWN) e sposta il puntatore (ACTION_MOVE). Il processo esegue il rendering nel doppio buffer quando il puntatore esce dalla superficie dello schermo (ACTION_UP).

Puoi utilizzare requestUnbufferedDispatch() per chiedere al sistema di input di non raggruppare gli eventi di movimento, ma di recapitarli non appena sono disponibili:

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;
}

Cosa fare e cosa non fare sul rendering

Cosa fare

Piccole parti dello schermo, scrittura a mano libera, disegno, disegno.

Cosa non fare

Aggiornamento dello schermo intero, panoramica e zoom. Possono causare lo strappo.

Strappo

Il strappo si verifica quando la schermata si aggiorna mentre il buffer dello schermo viene modificato contemporaneamente. Una parte dello schermo mostra i nuovi dati, mentre un'altra mostra quelli vecchi.

Le parti superiore e inferiore dell&#39;immagine di Android sono disallineate a causa dello strappo all&#39;aggiornamento dello schermo.
Figura 11. Il tearing con l'aggiornamento dello schermo dall'alto verso il basso.

Previsione movimento

La libreria di previsione del movimento Jetpack riduce la latenza percepita stimando il percorso del tratto dell'utente e fornendo punti artificiali temporanei al renderer.

La libreria di previsione del movimento riceve input utente reali come oggetti MotionEvent. Gli oggetti contengono informazioni su coordinate x e y, pressione e tempo, che vengono utilizzate dal predittore di movimento per prevedere oggetti MotionEvent futuri.

Gli oggetti MotionEvent previsti sono solo stime. Gli eventi previsti possono ridurre la latenza percepita, ma i dati previsti devono essere sostituiti con i dati effettivi di MotionEvent una volta ricevuti.

La libreria di previsione del movimento è disponibile su Android 4.4 (livello API 19) e versioni successive e sui dispositivi ChromeOS con Android 9 (livello API 28) e versioni successive.

La latenza causa un ritardo nella posizione dello stilo per il tratto sottoposto a rendering. Lo spazio tra il tratto e lo stilo viene riempito di punti di previsione. Il divario rimanente è la latenza percepita.
Figura 12. Latenza ridotta dalla previsione del movimento.

Dipendenze

La libreria di previsione del movimento fornisce l'implementazione della previsione. La libreria viene aggiunta come dipendenza nel file build.gradle del modulo dell'app:

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

Implementazione

La libreria di previsione del movimento include l'interfaccia MotionEventPredictor, che definisce i seguenti metodi:

  • record(): archivia MotionEvent oggetti come record delle azioni dell'utente
  • predict(): restituisce un valore MotionEvent previsto
Dichiara un'istanza di MotionEventPredictor

Kotlin

var motionEventPredictor = MotionEventPredictor.newInstance(view)

Java

MotionEventPredictor motionEventPredictor = MotionEventPredictor.newInstance(surfaceView);
Fornisci dati al predittore

Kotlin

motionEventPredictor.record(motionEvent)

Java

motionEventPredictor.record(motionEvent);
Previsione

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;
}

Cosa fare e cosa non fare con la previsione del movimento

Cosa fare

Rimuovi i punti di previsione quando viene aggiunto un nuovo punto previsto.

Cosa non fare

Non usare punti di previsione per il rendering finale.

App per creare note

ChromeOS consente alla tua app di dichiarare alcune azioni per la creazione di note.

Per registrare un'app come app per scrivere note su ChromeOS, vedi Compatibilità degli input.

Per registrare un'app come app per scrivere note su Android, vedi Creare un'app per creare note.

Android 14 (livello API 34) ha introdotto l'intent ACTION_CREATE_NOTE, che consente alla tua app di avviare un'attività per scrivere note dalla schermata di blocco.

Riconoscimento digitale di inchiostri con ML Kit

Con il riconoscimento di inchiostri digitali di ML Kit, la tua app può riconoscere il testo scritto a mano su una piattaforma digitale in centinaia di lingue. Puoi anche classificare gli schizzi.

ML Kit fornisce la classe Ink.Stroke.Builder per creare oggetti Ink che possono essere elaborati dai modelli di machine learning per convertire la scrittura a mano libera in testo.

Oltre al riconoscimento della scrittura a mano libera, il modello è in grado di riconoscere gesti, come l'eliminazione e il cerchio.

Per scoprire di più, consulta Riconoscimento digitale di inchiostro.

Risorse aggiuntive

Guide per gli sviluppatori

Codelab