1. Avant de commencer
SociaLite montre comment utiliser diverses API de plate-forme Android pour implémenter des fonctionnalités couramment utilisées dans les applications de réseaux sociaux, en tirant parti de plusieurs API Jetpack pour réaliser des fonctionnalités complexes qui fonctionnent de manière fiable sur plus d'appareils et nécessitent moins de code.
Cet atelier de programmation vous explique comment rendre l'application SociaLite compatible avec la mesure d'application bord à bord d'Android 15 et comment rendre votre application bord à bord de manière rétrocompatible. Une fois le bord à bord implémenté, SociaLite ressemblera à ce qui suit, en fonction de votre appareil et du mode de navigation :
SociaLite avec la navigation à trois boutons | SociaLite avec la navigation par gestes |
SociaLite sur un appareil à grand écran |
Conditions préalables
- Connaissances de base en Kotlin.
- Avoir terminé l'atelier de programmation Configurer Android Studio ou s'être familiarisé avec l'utilisation d'Android Studio et des applications de test dans un émulateur ou sur un appareil physique équipé d'Android 15.
Objectifs de l'atelier
- Comment gérer l'affichage bord à bord d'Android 15.
- Comment rendre votre application bord à bord de manière rétrocompatible.
Ce dont vous avez besoin
- La dernière version d'Android Studio.
- Un appareil de test ou un émulateur équipé de la version bêta 1 d'Android 15 ou version ultérieure.
- Un SDK équipé de la version bêta 1 d'Android 15 ou version ultérieure.
2. Télécharger le code de démarrage
- Téléchargez le code de démarrage sur GitHub.
Clonez le dépôt et consultez la branche codelab_improve_android_experience_2024
.
$ git clone git@github.com:android/socialite.git
$ cd socialite
$ git checkout codelab_improve_android_experience_2024
- Ouvrez SociaLite dans Android Studio et exécutez l'application sur votre appareil ou émulateur Android 15. L'écran qui s'affiche ressemble à ceci :
Navigation à trois boutons | Navigation par gestes |
Grand écran |
- Sur la page Chats (Discussions), sélectionnez l'une des conversations, telles que la conversation avec le chien.
Message instantané du chien avec la navigation à trois boutons | Message instantané du chien avec la navigation par gestes |
3. Rendre votre application bord à bord sur Android 15
Qu'est-ce que l'expérience bord à bord ?
Les applications peuvent afficher leur contenu derrière les barres système, ce qui permet d'améliorer l'expérience utilisateur et d'utiliser pleinement l'espace d'affichage. C'est ce qu'on appelle le bord à bord.
Comment gérer l'affichage bord à bord d'Android 15
Avant Android 15, l'interface utilisateur de votre application était limitée par défaut à un affichage qui évitait les zones de la barre système, comme la barre d'état et la barre de navigation. Les applications proposent désormais une expérience bord à bord. En fonction de l'application, cet affichage peut s'avérer plus ou moins compliqué à implémenter.
À partir d'Android 15, votre application sera par défaut bord à bord. Voici comment se comportera l'écran par défaut :
- La barre de navigation à trois boutons est translucide.
- La barre de navigation par gestes est transparente.
- La barre d'état est transparente.
- À moins que le contenu n'applique des encarts ou des marges intérieures, le contenu sera affiché derrière les barres système, comme la barre de navigation, la barre d'état et la barre de légende.
Cela permet de s'assurer que le bord à bord n'est pas utilisé comme moyen d'améliorer la qualité de l'application et de réduire le travail nécessaire pour que votre application soit bord à bord Cependant, ce changement peut affecter votre application. Vous verrez deux exemples d'impacts négatifs dans SociaLite après avoir mis à niveau le SDK cible vers Android 15.
Modifier la valeur du SDK cible en Android 15
- Dans le fichier build.gradle de l'application SociaLite, changez la cible et compilez les versions du SDK avec les valeurs pour Android 15 ou
VanillaIceCream
.
Si vous suivez cet atelier de programmation avant la sortie de la version stable d'Android 15, le code ressemblera à ceci :
android {
namespace = "com.google.android.samples.socialite"
compileSdkPreview = "VanillaIceCream"
defaultConfig {
applicationId = "com.google.android.samples.socialite"
minSdk = 21
targetSdkPreview = "VanillaIceCream"
...
}
...
}
Si vous suivez cet atelier de programmation après la sortie de la version stable d'Android 15, le code ressemblera à ceci :
android {
namespace = "com.google.android.samples.socialite"
compileSdk = 35
defaultConfig {
applicationId = "com.google.android.samples.socialite"
minSdk = 21
targetSdk = 35
...
}
...
}
- Recompilez SociaLite et observez les problèmes suivants :
- La protection de l'arrière-plan de la navigation à trois boutons ne correspond pas à la barre de navigation. L'écran Chats semble bord à bord sans aucune intervention de votre part pour la navigation par gestes. Toutefois, la protection de l'arrière-plan de la navigation à trois boutons devrait être supprimée.
Écran Chats avec la navigation à trois boutons | Écran Chats avec la navigation par gestes. |
- UI masquée. Dans une conversation, les éléments inférieurs d'interface utilisateur sont masqués par les barres de navigation. Cela est particulièrement évident dans le cas d'une navigation à trois boutons.
Message instantané du chien avec la navigation à trois boutons | Message instantané du chien avec la navigation par gestes |
Réparer SociaLite
Pour supprimer la protection par défaut de l'arrière-plan de la navigation à trois boutons, procédez comme suit :
- Dans le fichier
MainActivity.kt
, supprimez la protection par défaut de l'arrière-plan en définissant la propriété window.isNavigationBarContrastEnforced sur false (faux).
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
super.onCreate(savedInstanceState)
setContent {
// Add this block:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.isNavigationBarContrastEnforced = false
}
}
}
...
}
window.isNavigationBarContrastEnforced
garantit que la barre de navigation est suffisamment contrastée lorsqu'un arrière-plan entièrement transparent est nécessaire. En définissant cet attribut sur false, vous définissez en fait l'arrière-plan des trois boutons de navigation comme étant transparent. window.isNavigationBarContrastEnforced
n'aura d'incidence que sur la navigation à trois boutons et non sur la navigation par gestes.
- Relancez l'application et affichez l'une des conversations sur votre appareil Android 15. Les écrans Timeline (Historique), Chats (Discussions) et Settings (Paramètres) apparaissent maintenant tous bord à bord. La
NavigationBar
de l'application (avec les boutons Timeline, Chats et Settings) s'affiche derrière la barre de navigation transparente à trois boutons du système.
Écran Chats sans la bande | Aucune modification pour la navigation par gestes |
Notez toutefois que l'InputBar
(barre de saisie) de la conversation est toujours masquée par les barres système. Pour résoudre ce problème, vous devez gérer correctement les encarts.
Conversation du chien avec la navigation à trois boutons. Le champ de saisie situé en bas de l'écran est masqué par la barre de navigation du système. | Conversation du chien avec la navigation par gestes. Le champ de saisie situé en bas de l'écran est masqué par la barre de navigation du système. |
Dans SociaLite, l'InputBar
est masquée. En pratique, les éléments situés en haut, en bas, à gauche et à droite peuvent être masqués lorsque vous passez en mode Paysage ou que vous utilisez un appareil à grand écran. Vous devez réfléchir à la manière de gérer les encarts pour tous ces cas d'utilisation. Pour SociaLite, vous appliquez une marge intérieure afin d'augmenter le contenu pouvant être saisi dans l'InputBar
.
Pour appliquer des encarts afin de réparer des interfaces utilisateur masquées, procédez comme suit :
- Accédez au fichier
ui/chat/ChatScreen.kt
et trouvez le composableChatContent
autour de la ligne 178, qui contient l'UI de l'écran de conversation.ChatContent
profite deScaffold
pour construire facilement l'UI. Par défaut,Scaffold
fournit des informations sur l'UI du système, telles que la profondeur des barres système, sous forme d'encarts que vous pouvez utiliser avec les valeurs de marge intérieure deScaffold
(le paramètreinnerPadding
). Ajoutez une marge intérieure à l'aide d'innerPadding
deScaffold
surInputBar
. - Trouvez
InputBar
dansChatContent
aux alentours de la ligne 214. Il s'agit d'un composant personnalisé qui crée l'UI permettant aux utilisateurs de rédiger des messages. L'aperçu ressemble à ceci :
InputBar
prends un contentPadding
et l'applique comme marge intérieure au composable "Row" qui contient le reste de l'interface utilisateur. La marge intérieure sera appliquée à l'ensemble du composable "Row". C'est ce qui s'affiche au niveau de la ligne 432. Voici le composable InputBar
pour référence (n'ajoutez pas ce code) :
// Don't add this code because it's only for reference.
@Composable
private fun InputBar(
contentPadding: PaddingValues,
...,
) {
Surface(...) {
Row(
modifier = Modifier
.padding(contentPadding)
...
) {
IconButton(...) { ... } // take picture
IconButton(...) { ... } // attach picture
TextField(...) // write message
FilledIconButton(...){ ... } // send message
}
}
}
}
- Retournez à
InputBar
dansChatContent
et modifiezcontentPadding
de façon à utiliser les encarts de la barre système Vers la ligne 220.
InputBar(
...
contentPadding = innerPadding, //Add this line.
// contentPadding = PaddingValues(0.dp), // Remove this line.
...
)
- Exécutez à nouveau l'application sur votre appareil Android 15.
Conversation du chien avec la navigation à trois boutons et les encarts mal appliqués. | Conversation du chien avec la navigation par gestes et les encarts mal appliqués. |
La marge intérieure inférieure a été appliquée afin que les boutons ne soient plus masqués par les barres système. Cependant, la marge intérieure supérieure a également été appliquée. La marge intérieure supérieure englobe la profondeur de TopAppBar
et de la barre système. Scaffold transmet les valeurs de marge intérieure à son contenu afin qu'il puisse éviter la barre d'application supérieure ainsi que les barres système.
- Pour corriger la marge intérieure supérieure, créez une copie des
PaddingValues
d'innerPadding
, définissez la marge intérieure supérieure sur0.dp
, et transmettez votre copie modifiée danscontentPadding
.
InputBar(
...
contentPadding = innerPadding.copy(layoutDirection, top = 0.dp), //Add this line.
// contentPadding = innerPadding, // Remove this line.
...
)
- Exécutez à nouveau l'application sur votre appareil Android 15.
Conversation du chien avec la navigation à trois boutons et les encarts correctement appliqués. | Conversation du chien avec la navigation par gestes et les encarts correctement appliqués. |
Félicitations Vous avez rendu SociaLite compatible avec les modifications bord à bord des plates-formes sous Android 15 Vous allez maintenant apprendre à rendre SociaLite bord à bord de manière rétrocompatible.
4. Rendre SociaLite bord à bord de manière rétrocompatible
SociaLite est maintenant bord à bord sur Android 15, mais n'est toujours pas bord à bord sur les appareils Android plus anciens. Pour ce faire, appelez enableEdgeToEdge
avant de définir le contenu du fichier MainActivity.kt
.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
enableEdgeToEdge() // Add this line.
window.isNavigationBarContrastEnforced = false
super.onCreate(savedInstanceState)
setContent {... }
}
}
L'import pour enableEdgeToEdge
est import androidx.activity.enableEdgeToEdge
. La dépendance est AndroidX Activity 1.8.0 ou version ultérieure.
Consultez les guides suivants pour apprendre à rendre votre application bord à bord de manière rétrocompatible ainsi qu'à gérer les encarts :
La partie concernant le bord à bord est terminée. La section suivante est facultative et aborde d'autres considérations relatives au bord à bord qui pourraient s'appliquer à votre application.
5. Facultatif : considérations supplémentaires relatives au bord à bord
Gérer les encarts dans les architectures
Composants
Vous avez peut-être remarqué que de nombreux composants de SociaLite n'ont pas changé après que nous avons modifié la valeur du SDK cible. L'application SociaLite est conçue en suivant les meilleures pratiques, ce qui facilite la gestion de ce changement de plate-forme. En voici quelques-unes :
- Utilisez les composants de Material Design 3 (
androidx.compose.material3
), tels queTopAppBar
,BottomAppBar
etNavigationBar
, car ils appliquent automatiquement les encarts. - Si votre application utilise plutôt des composants Material 2 (
androidx.compose.material
) dans Compose, ces composants ne gèrent pas automatiquement les encarts eux-mêmes. Cependant, vous pouvez accéder aux encarts et les appliquer manuellement. Dansandroidx.compose.material 1.6.0
et version ultérieure, utilisez le paramètrewindowInsets
pour appliquer manuellement les encarts pourBottomAppBar
,TopAppBar
,BottomNavigation
, etNavigationRail
. De la même manière, utilisez le paramètrecontentWindowInsets
pourScaffold
. Dans le cas contraire, appliquez les encarts manuellement en tant que marge intérieure. - Si votre application utilise des vues et des composants Material (
com.google.android.material
), la plupart des composants Material basés sur des vues (tels queBottomNavigationView
,BottomAppBar
,NavigationRailView
etNavigationView
) gèrent les encarts et ne nécessitent donc aucun travail supplémentaire. Cependant, vous devrez ajouterandroid:fitsSystemWindows="true"
si vous utilisezAppBarLayout
. - Si votre application utilise les vues ainsi que
BottomSheet
,SideSheet
, ou des conteneurs personnalisés, appliquez la marge intérieure en utilisantViewCompat.setOnApplyWindowInsetsListener
. PourRecyclerView
, appliquez la marge intérieure en utilisant cet écouteur et ajoutez égalementclipToPadding="false"
. - Utilisez
Scaffold
(ouNavigationSuiteScaffold
, ou encoreListDetailPaneScaffold
), et nonSurface
, pour une UI complexe.Scaffold
vous laisse placer facilementTopAppBar
,BottomAppBar
,NavigationBar
etNavigationRail
.
Défilement du contenu
Votre application peut contenir des listes et le dernier élément de la liste peut être masqué par les barres de navigation du système avec le passage à Android 15.
L'affichage du dernier élément de la liste est masqué par la navigation à trois boutons.
Défilement du contenu avec Compose
Dans Compose, utilisez contentPadding de LazyColumn
pour ajouter un espace au dernier élément, à moins que vous n'utilisiez TextField
:
Scaffold { innerPadding ->
LazyColumn(
contentPadding = innerPadding
) {
// Content that does not contain TextField
}
}
L'affichage du dernier élément de la liste n'est pas masqué par la navigation à trois boutons.
Pour TextField
, utilisez un Spacer
pour afficher le dernier TextField
dans une LazyColumn
. Pour plus d'informations, consultez l'utilisation des encarts.
LazyColumn(
Modifier.imePadding()
) {
// Content with TextField
item {
Spacer(
Modifier.windowInsetsBottomHeight(
WindowInsets.systemBars
)
)
}
}
Défilement du contenu avec des vues
Pour RecyclerView
ou NestedScrollView
, ajoutez android:clipToPadding="false"
.
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:layoutManager="LinearLayoutManager" />
Ajoutez des marges intérieures à gauche, à droite et en bas des encarts de fenêtre en utilisant setOnApplyWindowInsetsListener
:
ViewCompat.setOnApplyWindowInsetsListener(binding.recycler) { v, insets ->
val i = insets.getInsets(
WindowInsetsCompat.Type.systemBars() + WindowInsetsCompat.Type.displayCutout()
)
v.updatePadding(
left = i.left,
right = i.right,
bottom = i.bottom + bottomPadding,
)
WindowInsetsCompat.CONSUMED
}
Utiliser LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
Avant de cibler le SDK 35, SocialLite ressemblait à ceci en mode Paysage, où le bord gauche comporte une grande boîte blanche pour tenir compte de l'encoche pour l'appareil photo. Dans le cas d'une navigation à trois boutons, les boutons se trouvent sur le côté droit.
Après avoir ciblé le SDK 35, SociaLite ressemblera à ceci, où le bord gauche n'a plus de grande boîte blanche pour tenir compte de l'encoche pour l'appareil photo. Pour ce faire, Android définit automatiquement LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS.
En fonction de votre application, vous pourriez vouloir gérer les encarts ici.
Suivez les étapes suivantes pour le faire dans SociaLife :
- Dans le fichier
ui/ContactRow.kt
, trouvez le composable "Row". - Modifiez la marge intérieure pour prendre en compte l'encoche.
@Composable
fun ChatRow(
chat: ChatDetail,
onClick: (() -> Unit)?,
modifier: Modifier = Modifier,
) {
// Add layoutDirection, displayCutout, startPadding, and endPadding.
val layoutDirection = LocalLayoutDirection.current
val displayCutout = WindowInsets.displayCutout.asPaddingValues()
val startPadding = displayCutout.calculateStartPadding(layoutDirection)
val endPadding = displayCutout.calculateEndPadding(layoutDirection)
Row(
modifier = modifier
...
// .padding(16.dp) // Remove this line.
// Add this block:
.padding(
PaddingValues(
top = 16.dp,
bottom = 16.dp,
// Ensure content is not occluded by display cutouts
// when rotating the device.
start = startPadding.coerceAtLeast(16.dp),
end = endPadding.coerceAtLeast(16.dp)
)
),
...
) { ... }
Une fois l'encoche prise en compte, SociaLite s'affiche comme suit :
Vous pouvez essayer différentes configurations d'encoche sur l'écran des Options pour les développeurs, sous Encoche.
Si votre application possède une fenêtre non flottante (par exemple, une activité) qui utilise LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
, LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
ou LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
, Android interprétera ces mode d'encoche comme étant LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
à partir de la version bêta 2 d'Android 15. Votre application aurait planté s'il s'agissait de la version bêta 1 d'Android 15.
Les barres de légende sont aussi des barres système
Une barre de légende est également une barre système, car elle décrit la décoration de la fenêtre de l'UI du système d'une fenêtre de forme libre, comme la barre de titre supérieure. Vous pouvez afficher la barre de légende dans un émulateur d'ordinateur dans Android Studio. Dans la capture d'écran suivante, la barre de légende se trouve en haut de l'application.
Dans Compose, si vous utilisez les PaddingValues
, safeContent
, safeDrawing
de Scaffold, ou les WindowInsets.systemBars
intégrées, votre application s'affichera comme prévu. Cependant, si vous gérez manuellement les encarts avec statusBar
, le contenu de votre application peut ne pas s'afficher comme prévu parce que la barre d'état ne tient pas compte de la barre de légende.
Dans les vues, si vous gérez manuellement les encarts avec WindowInsetsCompat.systemBars
, votre application s'affichera comme prévu. Si vous gérez manuellement les encarts avec WindowInsetsCompat.statusBars
, votre application peut ne pas s'afficher comme prévu, car les barres de statut ne sont pas des barres de légende.
Applications en mode immersif
Les écrans en mode immersif ne sont pas affectés par les mesures d'application bord à bord d'Android 15, car les applications immersives sont déjà bord à bord.
Protéger les barres système
Vous pouvez souhaiter que votre application ait une barre transparente pour la navigation par gestes, mais une barre translucide ou opaque pour la navigation à trois boutons.
Dans Android 15, une barre translucide de navigation à trois boutons est la valeur par défaut, car la plate-forme définit la propriété window.isNavigationBarContrastEnforced
sur true
. Celle de la navigation par gestes reste transparente.
La barre de la navigation à trois boutons est translucide par défaut. |
En règle général, une barre translucide de navigation à trois boutons suffit. Cependant, dans certains cas, votre application peut avoir besoin d'une barre opaque de navigation opaque à trois boutons. Commencez par définir la propriété window.isNavigationBarContrastEnforced
sur false
. Utilisez ensuite WindowInsetsCompat.tappableElement
pour les vues ou WindowInsets.tappableElement
pour Compose. Si la valeur affiche 0, alors l'utilisateur utilise la navigation par gestes. Autrement, il utilise la navigation à trois boutons. Si l'utilisateur utilise la navigation à trois boutons, affichez une vue ou une boîte derrière la barre de navigation. Un exemple pour Compose pourrait ressembler à ceci :
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
window.isNavigationBarContrastEnforced = false
MyTheme {
Surface(...) {
MyContent(...)
ProtectNavigationBar()
}
}
}
}
}
// Use only if required.
@Composable
fun ProtectNavigationBar(modifier: Modifier = Modifier) {
val density = LocalDensity.current
val tappableElement = WindowInsets.tappableElement
val bottomPixels = tappableElement.getBottom(density)
val usingTappableBars = remember(bottomPixels) {
bottomPixels != 0
}
val barHeight = remember(bottomPixels) {
tappableElement.asPaddingValues(density).calculateBottomPadding()
}
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Bottom
) {
if (usingTappableBars) {
Box(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.fillMaxWidth()
.height(barHeight)
)
}
}
}
Barre opaque de navigation à trois boutons |
6. Examiner le code de solution
La méthode onCreate
du fichier MainActivity.kt
devrait ressembler à cet extrait de code :
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
enableEdgeToEdge()
window.isNavigationBarContrastEnforced = false
super.onCreate(savedInstanceState)
setContent {
Main(
shortcutParams = extractShortcutParams(intent),
)
}
}
}
Le composable ChatContent
du fichier ChatScreen.kt
devrait gérer les encarts :
private fun ChatContent(...) {
...
Scaffold(...) { innerPadding ->
Column {
...
InputBar(
input = input,
onInputChanged = onInputChanged,
onSendClick = onSendClick,
onCameraClick = onCameraClick,
onPhotoPickerClick = onPhotoPickerClick,
contentPadding = innerPadding.copy(
layoutDirection, top = 0.dp
),
sendEnabled = sendEnabled,
modifier = Modifier
.fillMaxWidth()
.windowInsetsPadding(
WindowInsets.ime.exclude(WindowInsets.navigationBars)
),
)
}
}
}
Le code de solution est disponible dans la branche principale. Si vous avez déjà téléchargé SociaLite :
git checkout main
Sinon, vous pouvez à nouveau télécharger le code pour afficher la branche principale, soit directement, soit via Git :
git clone git@github.com:android/socialite.git