Quando i servizi di accessibilità navigano tra gli elementi sullo schermo, è importante che questi elementi siano raggruppati, separati o addirittura nascosti con la granularità corretta. Quando ogni singolo componente composable di basso livello nella schermata è evidenziato in modo indipendente, gli utenti devono interagire molto per spostarsi nella schermata. Se gli elementi vengono uniti in modo troppo aggressivo, gli utenti potrebbero non capire quali elementi appartengono logicamente l'uno all'altro. Se sullo schermo sono presenti elementi puramente decorativi, questi potrebbero essere nascosti ai servizi di accessibilità. In questi casi, puoi utilizzare le API Compose per unire, cancellare e nascondere la semantica.
Semantica dell'unione
Quando applichi un modificatore clickable
a un composable principale, Compose riunisce automaticamente tutti gli elementi secondari al suo interno. Per capire in che modo
i componenti Compose Material e Foundation interattivi utilizzano le strategie di unione
per impostazione predefinita, consulta la sezione Elementi interattivi.
È comune che un componente sia costituito da più composabili. Questi composabili potrebbero formare un gruppo logico e ognuno potrebbe contenere informazioni importanti, ma potresti comunque volere che i servizi di accessibilità li vedano come un unico elemento.
Ad esempio, immagina un composable che mostri l'avatar di un utente, il suo nome e alcune informazioni aggiuntive:

Puoi consentire a Compose di unire questi elementi utilizzando il parametro mergeDescendants
nel modificatore della semantica. In questo modo, i servizi di accessibilità trattano il componente come un'unica entità e tutte le proprietà semantiche dei discendenti vengono unite:
@Composable private fun PostMetadata(metadata: Metadata) { // Merge elements below for accessibility purposes Row(modifier = Modifier.semantics(mergeDescendants = true) {}) { Image( imageVector = Icons.Filled.AccountCircle, contentDescription = null // decorative ) Column { Text(metadata.author.name) Text("${metadata.date} • ${metadata.readTimeMinutes} min read") } } }
Ora i servizi di accessibilità si concentrano sull'intero contenitore contemporaneamente e ne uniscono i contenuti:

Ogni proprietà di semantica ha una strategia di unione definita. Ad esempio, la proprietà
ContentDescription
aggiunge tutti i valori ContentDescription
diretti
a un elenco. Puoi controllare la strategia di unione di una proprietà di semantica controllandone l'implementazione mergePolicy
in SemanticsProperties.kt.
Le proprietà possono assumere il valore principale o secondario, unire i valori in un elenco o una stringa, non consentire l'unione e generare un'eccezione oppure qualsiasi altra strategia di unione personalizzata.
Esistono altri scenari in cui ti aspetti che la semantica secondaria venga unita a quella principale, ma questo non accade. Nell'esempio seguente, abbiamo un elemento principale dell'elenco clickable
con elementi secondari e ci aspetteremmo che l'elemento principale li unisca tutti:

@Composable private fun ArticleListItem( openArticle: () -> Unit, addToBookmarks: () -> Unit, ) { Row(modifier = Modifier.clickable { openArticle() }) { // Merges with parent clickable: Icon( painter = painterResource(R.drawable.ic_logo), contentDescription = "Article thumbnail" ) ArticleDetails() // Defies the merge due to its own clickable: BookmarkButton(onClick = addToBookmarks) } }
Quando l'utente preme l'elemento clickable
Row
, si apre l'articolo. Al suo interno è presente un BookmarkButton
per aggiungere l'articolo ai preferiti. Questo pulsante nidificato
viene visualizzato come non unito, mentre il resto dei contenuti secondari all'interno della riga è
unito:

Row
. L'albero non unito contiene nodi separati per ogni Text
composablePer impostazione predefinita, alcuni composabili non vengono uniti automaticamente in un elemento principale. Un elemento primario non può unire i propri elementi secondari quando anche questi ultimi vengono uniti, impostando esplicitamente mergeDescendants = true
o essendo componenti che si uniscono, come pulsanti o elementi cliccabili. Sapere in che modo determinate API si fondono o si oppongono alla fusione può aiutarti a eseguire il debug di alcuni comportamenti potenzialmente imprevisti.
Utilizza l'unione quando gli elementi secondari costituiscono un gruppo logico e sensato
sotto l'elemento principale. Tuttavia, se gli elementi nidificati secondari richiedono la regolazione o la rimozione manuale della loro semantica, altre API potrebbero soddisfare meglio le tue esigenze (ad esempio,
clearAndSetSemantics
).
Cancella e imposta la semantica
Se le informazioni semantiche devono essere completamente cancellate o sovrascritte, è consigliabile utilizzare una potente API come clearAndSetSemantics
.
Quando un componente deve cancellare la propria semantica e quella dei suoi discendenti, utilizza questa API con un lambda vuoto. Quando la semantica deve essere sovrascritta, includi i nuovi contenuti all'interno della funzione lambda.
Tieni presente che, quando viene eseguita l'eliminazione con un lambda vuoto, la semantica eliminata non viene inviata a nessun consumatore che utilizza queste informazioni, come l'accessibilità, la compilazione automatica o i test. Quando sovrascrivi i contenuti con
clearAndSetSemantics{/*semantic information*/}
, la nuova semantica sostituisce tutte
le semantiche precedenti dell'elemento e dei suoi discendenti.
Di seguito è riportato un esempio di componente di pulsante di attivazione/disattivazione personalizzato, rappresentato da una riga interattiva con un'icona e un testo:
// Developer might intend this to be a toggleable. // Using `clearAndSetSemantics`, on the Row, a clickable modifier is applied, // a custom description is set, and a Role is applied. @Composable fun FavoriteToggle() { val checked = remember { mutableStateOf(true) } Row( modifier = Modifier .toggleable( value = checked.value, onValueChange = { checked.value = it } ) .clearAndSetSemantics { stateDescription = if (checked.value) "Favorited" else "Not favorited" toggleableState = ToggleableState(checked.value) role = Role.Switch }, ) { Icon( imageVector = Icons.Default.Favorite, contentDescription = null // not needed here ) Text("Favorite?") } }
Sebbene l'icona e il testo contengano alcune informazioni semantiche, insieme non indicano che questo componente è attivabile/disattivabile. L'unione non è sufficiente perché devi fornire informazioni aggiuntive sul componente.
Poiché lo snippet riportato sopra crea un componente di pulsante di attivazione/disattivazione personalizzato, devi aggiungere la funzionalità di attivazione/disattivazione, nonché le semantiche stateDescription
, toggleableState
e role
. In questo modo, lo stato del componente e l'azione associata sono disponibili. Ad esempio, TalkBack annuncia "Tocca due volte per attivare/disattivare" anziché "Tocca due volte per attivare".
Se elimini la semantica originale e ne imposti di nuove più descrittive, i servizi di accessibilità ora possono vedere che si tratta di un componente attivabile/disattivabile che può alternare lo stato.
Quando utilizzi clearAndSetSemantics
, tieni presente quanto segue:
- Poiché i servizi non ricevono informazioni quando questa API è impostata, è meglio usarla con parsimonia.
- Le informazioni semantiche possono essere potenzialmente utilizzate da agenti IA e servizi simili per comprendere la schermata e pertanto devono essere cancellate solo se necessario.
- La semantica personalizzata può essere impostata all'interno dell'API lambda.
- L'ordine dei modificatori è importante: questa API cancella tutta la semantica successiva al punto in cui viene applicata, indipendentemente da altre strategie di unione.
Nascondere la semantica
In alcuni scenari, gli elementi non devono essere inviati ai servizi di accessibilità, ad esempio perché le informazioni aggiuntive sono ridondanti per l'accessibilità o sono puramente decorative e non interattive. In questi casi, puoi nascondere gli elementi con l'API hideFromAccessibility
.
Nei seguenti esempi sono riportati i componenti che potrebbero dover essere nascosti: una filigrana ridondante che si estende su un componente e un carattere utilizzato per separare decorativamente le informazioni.
@Composable fun WatermarkExample( watermarkText: String, content: @Composable () -> Unit, ) { Box { WatermarkedContent() // Mark the watermark as hidden to accessibility services. WatermarkText( text = watermarkText, color = Color.Gray.copy(alpha = 0.5f), modifier = Modifier .align(Alignment.BottomEnd) .semantics { hideFromAccessibility() } ) } } @Composable fun DecorativeExample() { Text( modifier = Modifier.semantics { hideFromAccessibility() }, text = "A dot character that is used to decoratively separate information, like •" ) }
L'utilizzo di hideFromAccessibility
qui garantisce che la filigrana e la decorazione siano nascoste ai servizi di accessibilità, ma mantengano la loro semantica per altri casi d'uso, come i test.
Analisi dei casi d'uso
Di seguito è riportato un riepilogo dei casi d'uso per capire come distinguere chiaramente le API precedenti:
- Quando i contenuti non sono destinati all'utilizzo da parte dei servizi di accessibilità:
- Utilizza
hideFromAccessibility
quando i contenuti sono eventualmente decorativi o ridondanti, ma devono comunque essere testati. - Utilizza
clearAndSetSemantics{}
con una lambda vuota quando è necessario cancellare la semantica di elementi principali e secondari per tutti i servizi. - Utilizza
clearAndSetSemantics{/*content*/}
con i contenuti all'interno della funzione lambda quando è necessario impostare manualmente la semantica di un componente.
- Utilizza
- Quando i contenuti devono essere trattati come un'unica entità e richiedono che tutte le informazioni delle relative entità secondarie siano complete:
- Utilizza i discendenti semantici dell'unione.

Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Accessibilità in Componi
- [Material Design 2 in Compose][19]
- Testare il layout di Componi