Jetpack Compose

Zvuk při tapnutí na interaktivní prvky (Jetpack Compose)

Klasické Android View přehraje systémový klikací zvuk automaticky — performClick() si to obstará samo. Jetpack Compose ale žádnou View hierarchii v tomhle smyslu nepoužívá: onClick na Button, Modifier.clickable, toggleable ani selectable zvuk nepřehrají vůbec. Bez ručního doplnění je klepnutí úplně tiché — chybí standardní zvuková odezva, na kterou je uživatel Androidu zvyklý u každého ovládacího prvku.

1. Špatně — onClick bez zvukové odezvy

Button(
    onClick = {
        onSave(systolic.toInt(), diastolic.toInt(), pulse.toIntOrNull(), selectedTag, note.takeIf { showNote })
    },
    enabled = systolic.isNotBlank() && diastolic.isNotBlank(),
    modifier = Modifier.fillMaxWidth(),
) {
    Text(stringResource(R.string.save))
}

Funkčně v pořádku, ale klepnutí je potichu — Compose tu oproti klasickému View nic nepřehraje samo.

2. Správně — rememberClickSound() na začátku handleru

@Composable
fun rememberClickSound(): () -> Unit {
    val view = LocalView.current
    return remember(view) { { view.playClickSound() } }
}

fun View.playClickSound() {
    playSoundEffect(SoundEffectConstants.CLICK)
}
val playClick = rememberClickSound()

Button(
    onClick = {
        playClick()
        onSave(systolic.toInt(), diastolic.toInt(), pulse.toIntOrNull(), selectedTag, note.takeIf { showNote })
    },
    enabled = systolic.isNotBlank() && diastolic.isNotBlank(),
    modifier = Modifier.fillMaxWidth(),
) {
    Text(stringResource(R.string.save))
}

rememberClickSound() vrátí funkci, která zavolá View.playSoundEffect(SoundEffectConstants.CLICK) na aktuálním LocalView — to samé, co by klasické View udělalo samo přes performClick(). Zavolá se hned na začátku onClick, než handler udělá cokoli dalšího.

3. Kde se to v mých projektech používá

V mých aplikacích tohle používám v nastaveních pro lepší používání — u každé interakce, ne jen u tlačítek:

IconButton:

IconButton(onClick = { playClick(); coroutineScope.launch { drawerState.open() } }) {
    Icon(Icons.Filled.Menu, contentDescription = stringResource(R.string.open_menu))
}

Fokus na textové pole:

OutlinedTextField(
    value = systolic,
    onValueChange = { systolic = it.filter(Char::isDigit) },
    label = { Text(stringResource(R.string.systolic_label)) },
    singleLine = true,
    modifier = Modifier
        .fillMaxWidth()
        .focusRequester(systolicFocus)
        .onFocusChanged { if (it.isFocused) playClick() },
)

Stejný princip se týká i přepínačů, radio řádků a položek v menu/dropdownu — viz Přístupný přepínač (Switch) v Jetpack Compose, kde playClick() sedí v toggleable/selectable handleru stejným způsobem. Vlastní contentDescription nahrazující viditelný text řeší Vlastní contentDescription místo viditelného textu tlačítka (Jetpack Compose).

Proč to takhle je

  • LocalView.current + playSoundEffect(SoundEffectConstants.CLICK) — přesně dorovnává to, co klasické View dělá samo přes performClick(). Compose žádnou obdobu automaticky nevolá.
  • remember(view) { ... } — vrácená lambda se vytvoří jednou na daný LocalView, ne znovu při každé rekompozici.
  • Volá se jako první věc v handleru — nezávisí na tom, co handler dělá dál, a je to konzistentní místo, podle kterého jde snadno dohledat, že na daný prvek nikdo nezapomněl.
  • Netýká se to jen tlačítek — kterýkoli interaktivní prvek (přepínač, radio, položka menu, fokus na editovatelné pole) má u sebe stejný vzor.

Pravidla

Tohle není formální kritérium WCAG — WCAG cílí na web, ne na nativní Android komponenty. Jde o konzistenci s očekávaným chováním platformy: zvuková odezva na dotyk, kterou uživatel dostává u každého standardního Android ovládacího prvku, a kterou si Compose (na rozdíl od klasických View) sám nezajistí.

Shrnutí: každá interakce (tlačítko, přepínač, radio, položka menu, fokus na pole) zavolá rememberClickSound()-vrácenou funkci jako první věc v handleru. Bez toho je klepnutí v Compose potichu, i tam, kde by klasické View zvuk přehrálo samo.

Štítky

Komentáře

Zatím zde nejsou žádné komentáře. Buďte první, kdo napíše svůj názor.

Přidat komentář k „Zvuk při tapnutí na interaktivní prvky (Jetpack Compose)“