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řesperformClick(). 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.
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)“