Back Press Handling — je mechanismus zachycení a zpracování systémového tlačítka „Zpět” v Androidu, který určuje, jaká akce se provede při jeho stisknutí. Podle Android Developers (2024) byl od Androidu 11 tradiční metoda onBackPressed() nahrazena OnBackPressedDispatcher. Nové API umožňuje komponentám na jakékoli úrovni hierarchie zachytit stisk, nejen Activity. Klíčový rozdíl — podpora více callbacků v řetězci s prioritami.
Hlavní body
Back Press Handling — je systémový mechanismus Androidu, který určuje, co se stane, když uživatel stiskne hardwarové nebo softwarové tlačítko „Zpět”. V závislosti na kontextu může stisk: zavřít aktuální obrazovku a vrátit se na předchozí, skrýt klávesnici, zavřít Drawer nebo Bottom Sheet, ukončit aplikaci, pokud je uživatel na kořenové obrazovce.
Chování tlačítka „Zpět” se vyvíjelo s každou verzí Androidu. V Androidu 10 se objevila navigační gesta, v Androidu 11 — OnBackPressedDispatcher jako standardní API, a v Androidu 13 — vylepšená podpora predictive back gesture, kde systém zobrazuje animaci přechodu před skutečným provedením akce. Google se důsledně pohybuje směrem k předvídatelnému a konzistentnímu chování Back Press na všech zařízeních.
Správné zpracování tlačítka „Zpět” — kritický prvek UX v aplikaci pro Android. Uživatel očekává, že stisknutí „Zpět” ho vrátí na předchozí obrazovku v navigačním zásobníku, ne že neočekávaně ukončí aplikaci. Porušení tohoto očekávání je jedním z hlavních důvodů negativních recenzí a nízkého hodnocení v Google Play.
Historie Back Press API v Androidu odráží obecný vývoj platformy: od jednoduché metody v Activity po flexibilní systém callbacků s podporou lifecycle a Compose. Podívejme se na tři fáze vývoje.
Od úplně prvního Android API bylo tlačítko „Zpět” zpracováváno v metodě onBackPressed třídy Activity. Vývojář tuto metodu přepisoval a psal svou vlastní logiku. Problém byl v tom, že Fragment a View nemohly zachytit stisk — vše řízení šlo přes Activity. To vedlo k nafouklým Activity a složitému řetězci if-else pro určení toho, kdo by měl stisk zpracovat.
S Activity 1.0.0 (AndroidX) Google představil OnBackPressedDispatcher. To je centrální dispečer, který přijímá callbacky od jakékoli komponenty — Activity, Fragment, Dialog, vlastního View. Callbacky se registrují s určením pořadí (přes priority) a mohou být dynamicky přidávány nebo odebírány. OnBackPressedDispatcher je volán dříve než starý onBackPressed, což umožňuje zachytit stisk dříve, než ho zpracuje Activity.
Android 13 zavedl predictive back gesture — systémovou animaci, která ukazuje, kam vede stisknutí „Zpět”, ještě předtím, než uživatel dokončí gesto. Pro podporu této animace musí vývojáři používat OnBackPressedDispatcher a určit, zda callback podporuje systémovou animaci prostřednictvím vlastnosti isEnabled. Pokud callback nepodporuje predictive animation, systém zobrazí výchozí animaci, která nemusí odpovídat kontextu aplikace.
| API | Minimální SDK | Podpora Fragment | Predictive Back |
|---|---|---|---|
| onBackPressed | API Level 1 | Přes Activity | Ne |
| OnBackPressedDispatcher | Activity 1.0.0 | Přímá | Částečná |
| OnBackPressedDispatcher + lifecycle | Activity 1.3.0 | Lifecycle-aware | Plná |
OnBackPressedDispatcher — je jádrem nového Back Press API. Spravuje řetězec callbacků, volá je v pořadí až do prvního, který zpracuje událost. Pokud žádný callback nezpracuje stisk, dispečer provede výchozí akci — volání finish() pro Activity nebo popBackStack() pro Navigation Component.
Callback se registruje přes addCallback s uvedením LifecycleOwner a objektu OnBackPressedCallback. Callback obsahuje vlastnost isEnabled — pokud je nastavena na false, callback je přeskočen. Pro priority lze předat hodnotu od 0 (nejnižší) do Integer.MAX_VALUE. Fragment Activity Result API používá tento mechanismus pro automatickou registraci callbacků vázaných na životní cyklus.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val callback = object : OnBackPressedCallback(enabled = true) {
override fun handleOnBackPressed() {
if (isDrawerOpen) { closeDrawer() }
else { isEnabled = false; onBackPressed() }
}
}
onBackPressedDispatcher.addCallback(this, callback)
}
Callback je automaticky odstraněn, když LifecycleOwner přejde do stavu DESTROYED. To řeší starý problém úniků callbacků při otáčení obrazovky. Pokud je callback přidán do Fragmentu, je zaručeno, že bude odstraněn při zničení Fragmentu. Pro dočasné vypnutí callbacku se používá vlastnost isEnabled — lze ji přepínat bez odstranění a opětovné registrace.
Pořadí volání je obrácené pořadí přidávání: naposledy přidaný callback dostává řízení jako první. To je logické, protože nejvíce vnořený prvek UI (např. Bottom Sheet uvnitř Fragmentu) by měl zpracovat stisk dříve než nadřazený Fragment. Pokud nejhlubší callback nezpracovává stisk (isEnabled = false), řízení se předá dalšímu v řetězci.
Fragment API poskytuje vlastní integraci s OnBackPressedDispatcher prostřednictvím metody requireActivity().onBackPressedDispatcher. Od Fragmentu 1.2.0 může každý Fragment registrovat svůj vlastní callback, který se automaticky váže k životnímu cyklu Fragmentu a je odstraněn při jeho zničení.
Registrace callbacku ve Fragmentu probíhá v onCreate, onViewCreated nebo dokonce v samotném View — důležité je pouze, aby LifecycleOwner (Fragment) byl aktivní. Když Fragment přejde do stavu STARTED, callback se zapne, při STOPPED — vypne. To zaručuje, že skrytý Fragment (ve ViewPager) nebude zpracovávat stisk „Zpět”.
class EditorFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
showDiscardDialog()
}
}
requireActivity().onBackPressedDispatcher.addCallback(this, callback)
}
}
Dialogy a BottomSheet automaticky zachycují stisk „Zpět” pro zavření. Pokud potřebujete provést dodatečnou akci před zavřením — zaregistrujte callback s vyšší prioritou. Důležité: pokud jste nastavili setCancelable(false) na Dialog, callback nebude fungovat — to je systémové chování.
Jetpack Compose poskytuje deklarativní API pro zpracování tlačítka „Zpět” prostřednictvím composable funkce BackHandler. BackHandler přijímá enabled (booleovskou hodnotu) a onBack — callback, který je volán při stisku. Pokud je enabled = false, stisk je předán dále v řetězci.
BackHandler automaticky registruje OnBackPressedCallback v OnBackPressedDispatcher nadřazené Activity. Zohledňuje životní cyklus composable: při opuštění kompozice je callback odstraněn. enabled může být svázán se stavem — například zobrazovat potvrzovací dialog pouze v případě, že jsou ve formuláři neuložené změny.
@Composable
fun EditScreen(hasUnsavedChanges: Boolean) {
BackHandler(enabled = hasUnsavedChanges) {
// Zobrazit potvrzovací dialog
}
Column {
TextField(value = ..., onValueChange = ...)
}
}
Predictive back gesture v Compose je podporováno od Compose 1.5.0. BackHandler automaticky zpracovává systémovou animaci přechodu, pokud je na zařízení zapnuta. Pro vlastní predictive-animaci použijte modifikátor predictiveBackHandler, který vrací průběh gesta od 0 do 1.
Back Press Handling se zdá jednoduchý, ale v praxi vývojáři dělají řadu systematických chyb. Podívejme se na nejčastější problémy a způsoby jejich řešení na základě doporučení Google a zkušeností komunity.
Přímé volání finish() v handleOnBackPressed může vést k neočekávanému ukončení aplikace, pokud jsou v navigačním zásobníku obrazovky na pozadí. Vždy kontrolujte NavController.backStack prostřednictvím Navigation Component nebo Coordinator před zavřením Activity.
Pokud zaregistrujete callback bez LifecycleOwner (pomocí starého addCallback bez parametru), callback bude žít věčně a může způsobit NullPointerException, pokud je Activity již zničeno. Vždy používejte addCallback(this, callback) s LifecycleOwner.
Pro modální okna (Bottom Sheet, Dialog) vždy nastavujte isEnabled = true pouze když je okno viditelné. Použijte addCallback s lambdou, která kontroluje stav okna. Navigation Component to automaticky spravuje pro NavHost.
Rychlý dvojitý stisk „Zpět” může vést k dvojitému volání finish(). Použijte flag nebo throttleLast pro ochranu proti opakovaným voláním během 500 ms. Navigation Component vestavěně zpracovává tuto situaci, ale ve vlastních scénářích musíte implementovat ochranu ručně.
private var lastBackPressTime = 0L
override fun handleOnBackPressed() {
val currentTime = System.currentTimeMillis()
if (currentTime - lastBackPressTime > 500) {
lastBackPressTime = currentTime
navigateBack()
}
}
Často kladené otázky
onBackPressed se stal deprecated v Androidu 11, protože funguje pouze na úrovni Activity. OnBackPressedDispatcher umožňuje jakékoli komponentě (Fragment, Dialog, View) zachytit stisk prostřednictvím jednotného mechanismu s podporou životního cyklu.
Systém Android převádí gesto swipu od okraje na systémový stisk „Zpět” dříve, než ho aplikace obdrží. Na úrovni OnBackPressedDispatcher nelze tyto dvě události rozlišit — obě přicházejí jako handleOnBackPressed.
Ano, zpracování OnBackPressedDispatcher je stejné pro zařízení se třemi tlačítky a s gestovou navigací. Kód zachycení nezávisí na typu navigace — systém sám převádí gesto na volání dispečera.
Zapněte predictive back v Developer Options emulátoru Android 13+. Použijte ADB: `adb shell settings put global enable_back_animation 1`. Po zapnutí systémová animace zobrazí náhled přechodu při stisku „Zpět”.
Zkontrolujte dvě podmínky: LifecycleOwner musí být ve stavu STARTED nebo RESUMED a isEnabled callbacku musí být true. Pokud jsou obě podmínky splněny, ujistěte se, že callback byl přidán do správného OnBackPressedDispatcher — použijte requireActivity().onBackPressedDispatcher ve Fragmentu.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také