Comment intégrer le mode sombre dans une application Android

Gabriel TEKOMBO
AndroidMood
Published in
6 min readJun 10, 2020
Screenshots: Infotify News App

Devenu une fonctionnalité exploitée et couramment utilisée par de nombreuses applications mobiles, le mode sombre s’est énormément popularisé ces dernières années. Il offre une flexibilité indéniable en ce qu’il confère la faculté à un design d’être adaptable en fonction du temps qu’il fait et/ou des goûts des utilisateurs. Mais comment fonctionne-t-il? Et de quelle manière les développeurs doivent-t ’ils l’intégrer au sein de leur application ? Il sera question ici de présenter son fonctionnement dans le système Android, et expliquer comment est-ce qu’il peut être intégré comme fonctionnalité d’une application Android.

Les bases

1. Référencez toutes les couleurs dans colors.xml 🌈

C’est le tout premier point à remplir pour pouvoir appliquer un thème personnalisable et adaptable à votre application. Pour cela il est primordial de définir toutes les couleurs à utiliser dans le fichier colors.xml

L’astuce à adopter ici est de définir les couleurs de chacun de vos thèmes par bloc, ainsi, vous aurez par exemple :

<!--Light Color-->
<color name="colorPrimary">#FB8C00</color>
<
color name="colorPrimaryDark">#E65100</color>
<
color name="colorAccent">#448AFF</color>
<
color name="primaryTitleColor">#D9000000</color>
<
color name="primaryDescriptionColor">#8C000000</color>
<
color name="primaryDateColor">#afb5CA</color>
<
color name="primaryBackgroundColor">#F5F7FB</color>
<
color name="primaryCardBackgroundColor">#FFFFFF</color>
<
color name="primaryChipBackgroundColor">#eeeeee</color>
<
color name="primarySearchBackgroundColor">#F5F7FB</color>
<!--Night Color-->
<color name="colorPrimaryNight">#212121</color>
<
color name="colorPrimaryDarkNight">#000000</color>
<
color name="colorAccentNight">#f44336</color>
<
color name="primaryTitleColorNight">#FFFFFF</color>
<
color name="primaryDescriptionColorNight">#B3FFFFFF</color>
<
color name="primaryDateColorNight">#B3FFFFFF</color>
<
color name="primaryBackgroundColorNight">#303030</color>
<
color name="primaryCardBackgroundColorNight">#424242</color>
<
color name="primaryChipBackgroundColorNight">#616161</color>
<
color name="primarySearchBackgroundColorNight">#303030</color>

Une autre méthode si vous ne souhaitez pas définir vos thèmes (sombre et clair) dans le meme fichier est d’avoir de differentes définitions de vos couleurs et styles dans les repertoires res/values/ et res/values-night/, de maniere à pouvoir les modifier distinctement.

Dans notre exemple, les deux blocs sont «Light Color» qui représente les couleurs du thème par défaut et «Night Color» qui sera les couleurs de notre thème sombre. Passons au point suivant :

2. Créez les attributs de couleurs de vos thèmes dans le fichier attrs.xml 🎴

Pourquoi ? Créer vos propres attributs permettra d’affecter vos couleurs à vos vues, composants à partir du référencement de leur fichier de style.

Méthodologie : Créez un fichier de ressource dans le dossier res/values et nommez-le attrs.xml. En principe il n’existe pas par défaut lors de la création d’un projet Android, donc vous devrez le faire manuellement.

Vous aurez quelque chose comme ceci :

<?xml version=”1.0" encoding=”utf-8"?>
<resources>
</resources>

C’est à l’intérieur du fichier que vous insérerez vos différents attributs, c’est-à-dire l’ensemble des propriétés propre à votre design. Ceux-ci seront utilisés pour définir les couleurs de vos différents composants.

Syntaxe :

<attr name={NomDeVotreAttribut} format={TypDeVotreAttribut(color, dimension, integer etc…)}/>

En fonction de vos propriétés, aurez alors un fichier ressemblant à celui-ci :

<attr name="primaryTitleColor" format="color"/>
<
attr name="primaryDescriptionColor" format="color"/>
<
attr name="primaryDateColor" format="color"/>
<
attr name="primaryBackgroundColor" format="color"/>
<
attr name="primaryCardBackgroundColor" format="color"/>
<
attr name="primaryChipBackgroundColor" format="color"/>
<
attr name="iconBookmark" format="integer"/>

Nous avons donc créé des attributs qui nous permettrons de contrôler les couleurs assignées à nos layouts. Nous pouvons donc passer au 3e point :

3. Assignez les couleurs aux attributs dans le fichier styles.xml 🎯

Il sera question ici de créer deux thèmes bien distincts, c’est à dire Light et Night. Notre thème clair, et notre thème sombre, à partir des attributs et des couleurs définies plus haut.

<!-- Light application theme.-->
<style name=”AppTheme.Base.Light” parent=”Theme.AppCompat.Light.NoActionBar”>
<
item name=”colorPrimary”>@color/colorPrimary</item>
<
item name=”colorPrimaryDark”>@color/colorPrimaryDark</item>
<
item name=”colorAccent”>@color/colorAccent</item>
<
item name=”primaryTitleColor”>@color/primaryTitleColor</item>
<
item name=”primaryDescriptionColor”>@color/primaryDescriptionColor</item>
<
item name=”primaryBackgroundColor”>@color/primaryBackgroundColor</item>
<
item
</
style>

<!-- Night application theme.-->
<style name=”AppTheme.Base.Night” parent=”Theme.AppCompat.DayNight.NoActionBar”>
<
item name=”colorPrimary”>@color/colorPrimaryNight</item>
<
item name=”colorPrimaryDark”>@color/colorPrimaryDarkNight</item>
<
item name=”colorAccent”>@color/colorAccentNight</item>
<
item name=”primaryTitleColor”>@color/primaryTitleColorNight</item>
<
item name=”primaryDescriptionColor”>@color/primaryDescriptionColorNight</item>
<
item name=”primaryBackgroundColor”>@color/primaryBackgroundColorNight</item>

NB : Qu’il s’agisse d’un thème AppCompat ou MaterialComponents, Il est important de faire hériter vos thèmes de « Light » s’il s’agit du thème clair et de « NightDay » s’il s’agit du thème sombre, cela permet de signifier au système Android que le thème appliqué est en fait dédié à l’un ou l’autre. Cela se fait à partir de l’attribut « parent » de la balise <style>.

Dans notre cas, nos styles héritent de Theme.AppCompat.Light.NoActionBar et Theme.AppCompat.DayNight.NoActionBar

Nos avons donc nommé nos thèmes AppTheme.Base.Light et AppTheme.Base.Night.

Vous remarquerez que nous avons utilisé les attributs que nous avons créés dans attrs.xml et les couleurs issues de colors.xml pour créer nos styles personnalisés. A partir de là, nous assignerons donc les couleurs à nos composants avec la syntaxe « ?attr/CouleurDefinie » au lieu de « @color/CouleurDefinie ».

Un exemple illustratif :

<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:background="?attr/primaryBackgroundColor"
xmlns:android="http://schemas.android.com/apk/res/android" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="16sp"
android:textColor="?attr/primaryTitleColor"
android:text="Ceci est une app avec le mode sombre"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:layout_gravity="center"
android:textColor="?attr/primaryDescriptionColor"
android:text="Ceci est juste un autre TextView"/>
</LinearLayout>

De la même façon, dans vos fichiers de vecteurs à travers la propriété « fillColor », vous pourrez utiliser vos attributs personnalisés pour définir les couleurs et elles changeront en fonction du thème.

<vector android:height="24dp" android:tint="#C0C0C0"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<
path android:fillColor="?attr/primaryBackgroundColor" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
</
vector>

C’est une des raisons pour laquelle il est recommandé d’utiliser les fichiers XML pour vos images. A contrario, dans le cas où vous souhaitez définir des images différentes en fonctions du thème, il sera question pour vous d’utiliser le type d’attribut « Integer » dans votre fichier attrs.xml.

4. Appliquez vos thèmes! 🎨

De manière générale, un changement de thème s’effectue avec la methode setTheme() du SDK Android; de ce fait, il nous servira à appliquer nos thèmes au moment voulu à partir d’une action de l’utilisateur.

Selon le thème appliqué vous aurez un code ressemblant à ceci:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.AppTheme.Base.Night);
setContentView(R.layout.activity_main);
}

NB: La définition du thème s’éffectue TOUJOURS avant le chargement de la vue, ceci dans le but de notifier à l’application le style qu’il appliquera aux composants et aux differentes vues lors de leur création.

Android Studio offre nécessairement la possibilité de prévisualiser vos thèmes et ainsi permettre l’édition plus facile à partir de la fonctionnalité « Theme For Preview ». Ceci est d’autant plus avantageux pour des applications disposants de nombreux thèmes interchangeables.

Prévisualiser les differents themes de votre application en un clic
Theme For Preview

5. Derniere étape, juste un peu de code ! 👨‍💻

Nous allons tout d’abord prendre connaissance d’une specificité de gestion de thème sombre sur Android: AppCompatDelegate.

AppCompatDelegate est une classe de la bibliothèque de support d’Android qui nous fournit des méthodes spécifiquement pour gérer l’implémentation du thème sombre.
Il a donc une méthode statique appelée setDefaultNightMode(mode int) à laquelle nous pouvons passer le mode actuel qui doit être défini. Il peut prendre différents modes comme paramètres, nous avons:

  • MODE_NIGHT_NO.:Utilise toujours le thème clair (Light).
  • MODE_NIGHT_YES: Utilise toujours le mode sombre (Dark).
  • MODE_NIGHT_FOLLOW_SYSTEM (par défaut): Introduit depuis Android 9.0, ce paramètre suit le thème adopté par le système.
  • MODE_NIGHT_AUTO_BATTERY: Passe au mode sombre lorsque la fonction «Économiseur de batterie» est activée sur l’appareil, sinon reste clair.
  • MODE_NIGHT_AUTO_TIME & MODE_NIGHT_AUTO: Changements entre le thème sombre et clair en fonction de l’heure de la journée.

Cette notion acquise, nous pouvons donc passer au premier point de cette derniere étape:

  • Changer le thème en fonction d’une action de l’utilisateur

Il pourrait s’agir d’une action sur un Switch, un TextView, un RadioButton etc…

Dans cet exemple il est question de vérifier le thème actuel de l’application et de définir le thème sombre ou clair en fonction du cas échéant. Ensuite recréer l’application afin que le nouveau style soit appliqué.

  • Appliquer le nouveau thème

Nous appliquons donc le nouveau thème apres que l’activité ait été recréé.

Et le tour est joué ! 😎🎊🎊🎉🎉

Le changement de theme s’opere instantanement sur tous les composants affectés par la modification
Changement de thème 🎨

Bien evidement, arriver au resultat ci-dessus demande beaucoup plus de travail mais les bases sont deja posées.

Si vous souhaitez que le thème appliqué soit persistant, c’est-à-dire qu’il conserve son état même en cas de fermeture complète de l’application, il sera nécéssaire d’utiliser les préférences partagées.

Je vous invite à consulter le repo github de l’application Infotify, afin de mieux appréhender comment s’effectue implémentation du thème sombre avec persistance sur des interfaces complexes.

Merci pour votre attention, j’ose croire que cet article vous en a appris un peu plus sur l’implémentation des thèmes sur Android de maniere générale, et l’intégration du mode sombre en particulier. N’hesitez pas à laisser quelques applaudissements 👏 et à partager si ça vous a plu!

Suivez Android Mood pour ne louper aucun des articles à venir ! 😉

--

--

Gabriel TEKOMBO
AndroidMood

Android Engineer 📱. I write about Kotlin, Jetpack, best practices, engineering processes for mobile apps. — gabrielthecode.com