Edge-to-edge (SDK 36)#56
Open
Spriana wants to merge 6 commits into
Open
Conversation
Android 16 (targetSdk 36) impose le edge-to-edge et ignore l'opt-out windowOptOutEdgeToEdgeEnforcement, retiré ici. On gère donc les insets manuellement au lieu de compter sur fitsSystemWindows : padding du haut sur la toolbar (son fond colorPrimary peint la bande de barre d'état, icônes claires/sombres selon le thème), retrait de fitsSystemWindows des racines CoordinatorLayout et neutralisation de la gestion d'insets du DrawerLayout (sinon la toolbar ne peint pas la barre d'état et une marge grise apparaît en bas), padding du bas (barre de navigation + clavier) sur la barre d'envoi des topics, et padding du bas sur les listes pour garder le dernier élément au-dessus de la barre de navigation. Retrait aussi du scrim par défaut du ScrimInsetsFrameLayout du menu latéral. Vérifié sur Android 16. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ack=false Au targetSdk 36 le predictive back est activé par défaut et onBackPressed n'est plus appelé. On le désactive temporairement pour conserver les surcharges onBackPressed existantes (drawer, brouillons, retour WebView, double-retour) jusqu'à leur migration vers OnBackPressedDispatcher. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le edge-to-edge (géré par le commit précédent) et le predictive back (neutralisé) sont prêts. Les autres changements du SDK 36 (orientation grand écran, pages 16 Ko, réseau local, intents) ne concernent pas cette app. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…a barre de navigation Avec beaucoup de topics favoris la liste du menu latéral défile, et son dernier élément passait sous la barre de navigation. Le ScrimInsetsFrameLayout consommant les insets, on applique le padding bas (inset de barre de navigation) sur le panneau du menu (parent de la liste) plutôt que sur la liste. Vérifié sur Android 16 avec 11 favoris, navigation à 3 boutons. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…menu latéral Au-dessus du menu latéral ouvert, la barre de navigation paraissait opaque (le fond plein du panneau remplissait la zone) alors qu'elle est transparente sur les autres écrans. On applique désormais clipToPadding + padding bas sur la liste du menu plutôt que sur le panneau (le ScrimInsetsFrameLayout consommant les insets, le listener reste posé sur le panneau) : le contenu défile sous la barre comme ailleurs et le dernier élément reste au-dessus d'elle. Vérifié sur Android 16, navigation à 3 boutons. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sur Android < 15 (pas d'edge-to-edge), la barre d'état des écrans à menu latéral était grise : le hack statusBarNeedToBeTransparent la rendait transparente, en s'appuyant sur le scrim du DrawerLayout désormais neutralisé. On retire ce hack et on passe statusBarColor de colorPrimaryDark à colorPrimary, pour que la barre d'état ait la même couleur que la barre du haut, comme le rendu edge-to-edge des versions 15+. Vérifié sur Android 7.1.1 (API 25). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Presque pas de vérification au niveau du code/de la technique de ma part ; je n’ai pas cherché à comprendre si les commentaires laissés dans le code et le message du commit sont utiles. Ça marche, basta.
J’ai pas mal testé manuellement (navigation à gestes vs à trois boutons, Android 16 vs 14 (avant le edge-to-edge) vs 7.1.1, thèmes) et fait corrigé les erreurs trouvées, mais au vu de la montagne de changement il faudra prévenir les utilisateurs qu’il risque d’y avoir des bugs.
Graphiquement, le seul changement vraiment visible c’est que la couleur de la barre de statuts devient la même que la top bar (
android:statusBarColorest ignoré), y compris sur les vieilles versions d’Android exprès pour s’aligner sur les nouvelles. La couleur spécifique à la barre de statuts pourrait être conservée mais ça va à l’encontre de Material 3 ; et je trouve que c’est mieux sans sur les écrans modernes avec une barre de statuts épaisse.Ci-dessous les notes de mise en œuvre générées par Claude :
Cibler Android 16 (SDK 36) : edge-to-edge
Contexte : quand l'app cible le SDK 36, Android 16 impose le edge-to-edge et
l'opt-out
windowOptOutEdgeToEdgeEnforcementqu'on utilisait est ignoré. Cette notedécrit comment RespawnIRC gère le edge-to-edge après la migration. Elle a été réécrite
après un test sur un vrai appareil Android 16, donc elle reflète ce qui a réellement
été livré — une première version décrivait une approche qui n'a pas survécu aux tests
sur appareil.
L'idée de base
Les widgets AndroidX historiques (
CoordinatorLayout,DrawerLayout) essaientd'« aider » avec les insets via
android:fitsSystemWindows, et c'est précisément cetteaide qui casse tout sous edge-to-edge imposé :
fitsSystemWindowsduCoordinatorLayoutconsomme l'inset du haut, donc latoolbar ne peut pas peindre derrière la barre d'état → la barre d'état affiche le fond
de fenêtre (clair) avec des icônes blanches = une barre d'état blanche / invisible ;
DrawerLayoutconsomme les insets et réserve une marge en bas → une grosse bandegrise morte en bas.
L'approche est donc : arrêter de compter sur
fitsSystemWindows, le retirer deslayouts racines, et appliquer les insets nous-mêmes — peindre la bande de barre d'état
via la toolbar, et réserver la place de la barre de navigation / du clavier seulement
là où c'est nécessaire.
Pourquoi l'opt-out a disparu
En ciblant le SDK 36,
android:windowOptOutEdgeToEdgeEnforcementest déprécié et ignorésur Android 16 (encore respecté sur Android 15). Il est retiré de
styles.xml.android:statusBarColorest conservé et mis à?attr/colorPrimary: il est ignorésous edge-to-edge (15+, où c'est la toolbar qui peint la bande), mais sous Android 15 (où
le système ne l'impose pas) c'est lui qui colore la barre d'état encore opaque — utiliser
colorPrimary(etnon l'ancien
colorPrimaryDark) garde la barre de la même couleur que la barre du haut,comme en 15+. L'ancien hack
setStatusBarColor(TRANSPARENT)sur les écrans à menu latérala été retiré : il s'appuyait sur le scrim du
DrawerLayoutdésormais neutralisé, donc sur≤14 il laissait la barre afficher le fond de fenêtre gris.
Haut : la bande de barre d'état
Tous les écrans incluent
@layout/toolbaret passent parAbsToolbarActivity.initToolbar(), donc le haut est géré de façon centralisée :android:fitsSystemWindows="true"de chaque racineCoordinatorLayout(~15 layouts). Sans lui, l'inset atteint la toolbar au lieu d'être avalé par le
Coordinator.
toolbar.xml, la hauteur passe àwrap_content+android:minHeight="?attr/actionBarSize"pour que le padding du haut agrandisse labarre au lieu d'écraser son contenu.
initToolbar(), ajouter l'inset du haut en padding sur la toolbar — son fondcolorPrimaryremplit alors la bande de barre d'état :Contraste des icônes (
AbsThemedActivity) : icônes sombres uniquement pour le thèmeclair à couleur primaire claire (signal
toolbarTextColorIsInverteddéjà existant),blanches sinon :
Écrans à menu latéral (showforum, selectforum)
Le
DrawerLayoutconsomme les insets et réserve des marges, ce qui à la fois gênel'approche par toolbar (barre d'état) et crée une bande morte en bas. On le neutralise
pour que ces écrans se comportent comme les autres :
fitsSystemWindowsde la racine, et dansAbsNavigationViewActivityremplacer la gestion d'insets du
DrawerLayoutpar un listener pass-through :Les insets atteignent alors la toolbar (qui peint la bande) et le panneau du menu, et
plus rien ne réserve de marge en bas.
Sur le panneau (
ScrimInsetsFrameLayout), mettreapp:insetForeground="@android:color/transparent". Piège : retirer l'attributne suffit pas — le widget retombe sur un scrim sombre par défaut, qui apparaît sous
forme de bandes grises en haut et en bas du menu ouvert. Il faut le mettre
explicitement transparent.
La liste du menu elle-même prend l'inset de barre de navigation pour que son dernier
élément (avec beaucoup de favoris) passe au-dessus de la barre et que le contenu
défile dessous (barre transparente) comme les autres listes. Le
ScrimInsetsFrameLayoutconsommant les insets, on pose le listener sur le panneau mais on padde la liste
(
clipToPadding=false) :Padder le panneau laisserait la liste en retrait et son fond opaque ressemblerait à une
barre pleine ; padder la liste laisse le contenu défiler sous la barre transparente.
Bas : barre de navigation et clavier
Il n'y a aucun padding bas global (une première tentative de padding centralisé sur
android.R.id.contentcréait une grosse bande grise morte sur tous les écrans, pire ennavigation gestuelle). À la place :
ShowTopicActivity) : padding demax(navigationBars, ime)pour qu'elle reste au-dessus de la barre de navigation etmonte au-dessus du clavier ; son fond déborde sous la barre de navigation
(transparente) :
Utils.addBottomNavInsetPadding(view)metclipToPadding=falseet ajoute l'inset de barre de navigation en padding bas — lecontenu défile sous la barre, mais le dernier élément peut passer au-dessus (marche
en navigation gestuelle et à 3 boutons) :
Appliqué à : la liste des topics d'un forum, le sélecteur de forum (liste + scrollview
de la liste de base), les résultats de recherche, la liste des ignorés, la liste des
comptes, la gestion de sondage, l'affichage d'un message, d'un sondage, des infos d'un
forum, et la liste des préférences (
SettingsFragmentviagetListView()).Pas appliqué à la liste des messages d'un topic : elle est au-dessus de la barre
d'envoi (
layout_above), qui la sépare déjà de la barre de navigation — la padderlaisserait un trou.
Pièges constatés sur appareil
fitsSystemWindowsdesCoordinatorLayout/DrawerLayoutdoit être retiré (et leDrawerLayoutneutralisé activement) — le garder donne la barre d'état blanche / labande morte en bas.
gestuelle. Padder seulement les vraies barres du bas ; utiliser
clipToPadding=falsesur les listes.
ScrimInsetsFrameLayouta besoin d'uninsetForegroundmis à transparent, pasretiré.
ScrimInsetsFrameLayoutdu menu), padder la liste, pas le conteneur — padder leconteneur opaque ressemble à une barre pleine.
statusBarColor; unsetStatusBarColor(TRANSPARENT)oublié la laisse afficher le fondde fenêtre gris.
code et à la compilation — seul le lancement de l'app les révèle.
Matrice de tests
Tester sur un appareil/émulateur avec les 4 thèmes (Clair / Gris / Sombre / Noir) et les
deux modes de navigation (gestuelle + 3 boutons), en portrait et en paysage :
beaucoup de favoris, le dernier élément passe au-dessus de la barre et le contenu défile
dessous ;
minSdk) : barre d'état
colorPrimary, pas grise. La navigation gestuelle (Android 10+)rend déjà la barre de navigation transparente, donc la gestion d'insets du bas s'applique
aussi avant 15 — ce n'est pas une bascule « 15+ uniquement » nette.
Predictive back & passage au SDK (commits séparés)
onBackPressed()n'est plusappelé. L'app conserve ses surcharges
onBackPressed()pour l'instant viaandroid:enableOnBackInvokedCallback="false"dans le manifeste (migration versOnBackPressedDispatcherplus tard).targetSdk/compileSdk 36est un commit séparé ; c'est lui qui impose leedge-to-edge (géré ci-dessus) et active le predictive back (neutralisé ci-dessus).