Valori predefiniti dell'API

Le API Material, Compose UI e Foundation implementano e offrono molte pratiche accessibili per impostazione predefinita. Contengono una semantica integrata che segue il loro ruolo e la loro funzione specifici, il che significa che la maggior parte del supporto per l'accessibilità viene fornito con poco o nessun lavoro aggiuntivo.

L'utilizzo delle API appropriate per lo scopo appropriato in genere significa che i componenti sono dotati di comportamenti di accessibilità predefiniti che coprono i casi d'uso standard, ma ricordati di verificare se queste impostazioni predefinite soddisfano le tue esigenze di accessibilità. In caso contrario, Compose offre anche modi per soddisfare requisiti più specifici.

Conoscere la semantica e i pattern di accessibilità predefiniti nelle API Compose aiuta a comprendere come utilizzarle tenendo presente l'accessibilità, nonché a supportare l'accessibilità in componenti più personalizzati.

Dimensioni minime dei touch target

Ogni elemento sullo schermo che è possibile selezionare, toccare o con cui è possibile interagire deve essere abbastanza grande da consentire un'opportuna interazione. Quando imposti le dimensioni di questi elementi, assicurati di impostare la dimensione minima su 48 dp per rispettare correttamente le linee guida sull'accessibilità di Material Design.

I componenti Material, come Checkbox, RadioButton, Switch, Slider e Surface, impostano questa dimensione minima internamente, ma solo quando il componente può ricevere azioni utente. Ad esempio, quando un Checkbox ha il parametro onCheckedChange impostato su un valore non nullo, la casella di controllo include un padding per avere una larghezza e un'altezza di almeno 48 dp.

@Composable
private fun CheckableCheckbox() {
    Checkbox(checked = true, onCheckedChange = {})
}

Una casella di controllo con il padding predefinito con una larghezza e un'altezza di 48 dp.
Figura 1. Una casella di controllo con spaziatura predefinita.

Quando il parametro onCheckedChange è impostato su null, il padding non viene incluso perché non è possibile interagire direttamente con il componente.

@Composable
private fun NonClickableCheckbox() {
    Checkbox(checked = true, onCheckedChange = null)
}

Una casella di controllo senza spaziatura interna.
Figura 2. Una casella di controllo senza spaziatura interna.

Quando implementi controlli di selezione come Switch, RadioButton o Checkbox, in genere trasferisci il comportamento cliccabile a un contenitore principale impostando il callback del clic sul componibile su null e aggiungendo un modificatore toggleable o selectable al componibile principale.

@Composable
private fun CheckableRow() {
    MaterialTheme {
        var checked by remember { mutableStateOf(false) }
        Row(
            Modifier
                .toggleable(
                    value = checked,
                    role = Role.Checkbox,
                    onValueChange = { checked = !checked }
                )
                .padding(16.dp)
                .fillMaxWidth()
        ) {
            Text("Option", Modifier.weight(1f))
            Checkbox(checked = checked, onCheckedChange = null)
        }
    }
}

Una casella di controllo accanto al testo "Opzione" che viene selezionata e deselezionata.
Figura 3. Una casella di controllo con comportamento cliccabile.

Quando le dimensioni di un componibile selezionabile sono inferiori alle dimensioni minime del touch target, Compose aumenta comunque le dimensioni del touch target. Ciò avviene espandendo le dimensioni del target di tocco al di fuori dei confini del composable.

Il seguente esempio contiene un Box molto piccolo cliccabile. L'area di destinazione del tocco viene espansa automaticamente oltre i confini del Box, quindi il tocco accanto al Box attiva comunque l'evento di clic.

@Composable
private fun SmallBox() {
    var clicked by remember { mutableStateOf(false) }
    Box(
        Modifier
            .size(100.dp)
            .background(if (clicked) Color.DarkGray else Color.LightGray)
    ) {
        Box(
            Modifier
                .align(Alignment.Center)
                .clickable { clicked = !clicked }
                .background(Color.Black)
                .size(1.dp)
        )
    }
}

Una casella molto piccola su cui è possibile fare clic che si espande in un target di tocco più grande toccando accanto alla casella.
Figura 4. Una casella molto piccola su cui è possibile fare clic che viene espansa in un target di tocco più grande.

Per evitare possibili sovrapposizioni tra le aree di tocco di composabili diversi, utilizza sempre una dimensione minima sufficientemente grande per il composable. Nell'esempio, significa utilizzare il modificatore sizeIn per impostare la dimensione minima della casella interna:

@Composable
private fun LargeBox() {
    var clicked by remember { mutableStateOf(false) }
    Box(
        Modifier
            .size(100.dp)
            .background(if (clicked) Color.DarkGray else Color.LightGray)
    ) {
        Box(
            Modifier
                .align(Alignment.Center)
                .clickable { clicked = !clicked }
                .background(Color.Black)
                .sizeIn(minWidth = 48.dp, minHeight = 48.dp)
        )
    }
}

La casella molto piccola dell'esempio precedente viene aumentata di dimensioni per creare un target di tocco più grande.
Figura 5. Un target di tocco della casella più grande.

Elementi grafici

Quando definisci un composable Image o Icon, non esiste un modo automatico per il framework Android di capire cosa viene visualizzato dall'app. Devi passare una descrizione testuale dell'elemento grafico.

Immagina una schermata in cui l'utente può condividere la pagina corrente con gli amici. Questa schermata contiene un'icona di condivisione cliccabile:

Una striscia di quattro icone cliccabili, con l'icona "Condividi" evidenziata.
Figura 6. Una riga di icone cliccabili con l'icona "Condividi" selezionata.

Basandosi solo sull'icona, il framework Android non può descriverla a un utente con disabilità visive. Il framework Android ha bisogno di una descrizione testuale aggiuntiva dell'icona.

Il parametro contentDescription descrive un elemento grafico. Utilizza una stringa localizzata, in quanto visibile all'utente.

@Composable
private fun ShareButton(onClick: () -> Unit) {
    IconButton(onClick = onClick) {
        Icon(
            imageVector = Icons.Filled.Share,
            contentDescription = stringResource(R.string.label_share)
        )
    }
}

Alcuni elementi grafici sono puramente decorativi e potresti non volerli comunicare all'utente. Quando imposti il parametro contentDescription su null, indichi al framework Android che questo elemento non ha azioni o stato associati.

@Composable
private fun PostImage(post: Post, modifier: Modifier = Modifier) {
    val image = post.imageThumb ?: painterResource(R.drawable.placeholder_1_1)

    Image(
        painter = image,
        // Specify that this image has no semantic meaning
        contentDescription = null,
        modifier = modifier
            .size(40.dp, 40.dp)
            .clip(MaterialTheme.shapes.small)
    )
}

contentDescription è pensato principalmente per essere utilizzato per elementi grafici, come le immagini. I componenti di materiale, come Button o Text, e i comportamenti utili, come clickable o toggleable, sono dotati di altre semantiche predefinite che descrivono il loro comportamento intrinseco e possono essere modificati tramite altre API Compose.

Elementi interattivi

Le API Material e Foundation Compose creano elementi dell'interfaccia utente con cui gli utenti possono interagire tramite le API dei modificatori clickable e toggleable. Poiché i componenti interattivi possono essere costituiti da più elementi, clickable e toggleable uniscono la semantica dei relativi elementi secondari per impostazione predefinita, in modo che il componente venga trattato come un'entità logica.

Ad esempio, un materiale Button potrebbe essere costituito da un'icona secondaria e del testo. Invece di trattare gli elementi secondari come singoli elementi, un pulsante Material unisce la loro semantica per impostazione predefinita, in modo che i servizi di accessibilità possano raggrupparli di conseguenza:

Pulsanti con semantica di elementi secondari uniti o non uniti.
Figura 7. Pulsanti con semantica di elementi secondari uniti e non uniti.

Analogamente, l'utilizzo del modificatore clickable fa sì che un composable riunisca anche la semantica dei suoi discendenti in un'unica entità, che viene inviata ai servizi di accessibilità con una rappresentazione dell'azione corrispondente:

Row(
    // Uses `mergeDescendants = true` under the hood
    modifier = Modifier.clickable { openArticle() }
) {
    Icon(
        painter = painterResource(R.drawable.ic_logo),
        contentDescription = "Open",
    )
    Text("Accessibility in Compose")
}

Puoi anche impostare un onClickLabel specifico sull'elemento principale cliccabile per fornire informazioni aggiuntive ai servizi di accessibilità e offrire una rappresentazione più raffinata dell'azione:

Row(
    modifier = Modifier
        .clickable(onClickLabel = "Open this article") {
            openArticle()
        }
) {
    Icon(
        painter = painterResource(R.drawable.ic_logo),
        contentDescription = "Open"
    )
    Text("Accessibility in Compose")
}

Utilizzando TalkBack come esempio, questo modificatore clickable e la relativa etichetta di clic consentirebbero a TalkBack di fornire un suggerimento di azione "Tocca due volte per aprire questo articolo", anziché il feedback predefinito più generico "Tocca due volte per attivare".

Questo feedback cambia in base al tipo di azione. Un clic prolungato fornisce un suggerimento di TalkBack "Tocca due volte e tieni premuto per", seguito da un'etichetta:

Row(
    modifier = Modifier
        .combinedClickable(
            onLongClickLabel = "Bookmark this article",
            onLongClick = { addToBookmarks() },
            onClickLabel = "Open this article",
            onClick = { openArticle() },
        )
) {}

In alcuni casi, potresti non avere accesso diretto al modificatore clickable (ad esempio, quando è impostato in un livello nidificato inferiore),ma voler comunque cambiare l'etichetta dell'annuncio rispetto al valore predefinito. A questo scopo, separa l'impostazione di clickable dalla modifica dell'annuncio utilizzando il modificatore semantics e imposta l'etichetta del clic lì per modificare la rappresentazione dell'azione:

@Composable
private fun ArticleList(openArticle: () -> Unit) {
    NestedArticleListItem(
        // Clickable is set separately, in a nested layer:
        onClickAction = openArticle,
        // Semantics are set here:
        modifier = Modifier.semantics {
            onClick(
                label = "Open this article",
                action = {
                    // Not needed here: openArticle()
                    true
                }
            )
        }
    )
}

In questo caso, non è necessario passare l'azione di clic due volte, poiché le API Compose esistenti, come clickable o Button, si occupano di questo aspetto. Questo perché la logica di unione garantisce che l'etichetta e l'azione del modificatore più esterno vengano prese in considerazione per le informazioni presenti.

Nell'esempio precedente, l'azione di clic openArticle() viene passata in profondità da NestedArticleListItem automaticamente alla sua semantica clickable e può essere lasciata nulla nella seconda azione di modifica della semantica. Tuttavia, l'etichetta dei clic viene presa dal secondo modificatore di semanticaonClick(label = "Open this article"), in quanto non era presente nel primo.

Potresti riscontrare scenari in cui ti aspetti che la semantica secondaria venga unita a quella principale, ma ciò non accade. Per informazioni più dettagliate, consulta la sezione Unione ed eliminazione.

Componenti personalizzati

Come regola generale, per i componenti personalizzati, esamina l'implementazione di un componente simile nella libreria Material o in altre librerie Compose e imita o modifica il relativo comportamento di accessibilità, se opportuno.

Ad esempio, se stai sostituendo il componente Material Checkbox con la tua implementazione, esaminare l'implementazione esistente della casella di controllo ti ricorderà di aggiungere il modificatore triStateToggleable, che gestisce le proprietà di accessibilità per questo componente.

Inoltre, utilizza ampiamente i modificatori di Foundation, in quanto includono considerazioni sull'accessibilità e pratiche di composizione esistenti trattate in questa sezione.

Puoi anche trovare un esempio di componente di pulsante di attivazione/disattivazione personalizzato nella sezione Semantika di impostazione e eliminazione, nonché informazioni più dettagliate su come supportare l'accessibilità nei componenti personalizzati nelle linee guida per le API.