ADAMH:
1) Tohle vypadá na bug, jestli jsi do toho opravdu nijak nezasáhl. Zkus jinou verzi Android Studia, Build Tools, SDK... Nebo si to prostě udělej ručně podle nějakého tutorialu.
2) Pokud používáš výhradně navigation components, tak se backstack řídí atributem popUpTo. Prakticky ti pokryje všechny běžné scénáře v momentě, kdy pochopíš root node (u tebe to bude asi @id/mobile_navigation, prostě ID elementu navigation ve tvém navigačním grafu) a popUpToInclusive. Je to ovšem poměrně neintuitivní, takže se obrň trpělivostí, a navíc to neřeší některé edge casy, typicky na starších Androidech. Takže se můžeš dostat do situace, kdy musíš přetížit onBackPressed a/nebo onSupportNavigateUp v aktivitě (není to totéž, i když se to tak bohužel v praxi často používá).
Dám ti na to i příklady, ale jen v Kotlinu, protože jsem líný zrovna tohle přepisovat do Javy, kde by to byl nesrovnatelně delší kód.
Mám dva jednoduché interfacy, které můžou a nemusí Fragmenty implementovat:
interface IOnBackPressed {
fun onBackPressed(): Boolean
}
interface IOnUpPressed {
fun onUpPressed(): Boolean
}
Aktivita se vždycky zeptá Fragmentu, jestli si to ohandluje sám:
override fun onBackPressed() {
val fragment = nav_host_fragment?.childFragmentManager?.primaryNavigationFragment
// Teď se zeptám jestli mám vůbec referenci na aktivní fragment, jestli implementuje IOnBackPressed, a pokud ano, tak jestli ho ohandloval/zkonzumoval
if ((fragment as? IOnBackPressed)?.onBackPressed() != true) {
super.onBackPressed()
}
}
override fun onSupportNavigateUp(): Boolean {
val fragment = nav_host_fragment?.childFragmentManager?.primaryNavigationFragment
return if ((fragment as? IOnUpPressed)?.onUpPressed() != true) {
// Tady se ještě zeptám navControlleru, jestli se měl kam vrátit; pokud ne, tak byl uživatel v rootu a ve skutečnosti kliknul v toolbaru nikoli na šipku, ale na ikonu menu, takže potřebuju otevřít hamburger menu.
// Ne vždycky se to musí řešit takhle, ale já mám v tomhle projektu z businessových požadavků dost komplikovanou definici rootu :-D
if (navController.navigateUp(appBarConfig)) {
true
} else {
drawer_layout.openDrawer(GravityCompat.START)
false
}
} else {
true
}
}
A jednotlivé fragmenty mi oznámí, jestli mají nějakou tu výjimku. Pokud ji nemají vůbec, tak ty interfacy vůbec neimplementuju, ale můžou prostě vracet false:
// Tenhle fragment se vždycky šipkou v toolbaru vrací na homescreen, ať se tam šlo odkudkoli, neboť si to klient přál a na legacy Androidech zlobí popUpTo do rootu
override fun onUpPressed(): Boolean {
navController.navigate(ResultFragmentDirections.goToHome())
return true
}
// Tenhle fragment používá na přáni klienta custom klávesnici, takže emuluji chování té systémové, která se tlačítkem zpět nejprve schová (a hodnotou true zabrání aktivitě návrat ve stacku) a až když je schovaná, tak dalším stiskem vyhodí false a nechá aktivitu dělat její práci.
// To je třeba hezký příklad toho, že back (tlačítko/gesto) není to samé jako up (šipka v toolbaru), protože tam se vracíme rovnou, klávesnice neklávesnice:
override fun onBackPressed(): Boolean {
if (viewModel.keyboardShown.get()) {
viewModel.keyboardShown.set(false)
return true
}
return false
}
Jak vidíš z těch příkladů, jsou to vážně edge cases, které v zásadě odporují konvencím platformy. Když ti nedýchá na záda klient, měl by sis vystačit s navigačním grafem.