5.5 Dark Mode
Mit Android 10 hat Google einen systemweiten Dunkelmodus eingeführt. System UI und Apps werden auf Wunsch (ab Android 11 auch zeitgesteuert) dunkel dargestellt. Je nach Displaytechnologie lässt sich mit dem Dark Mode einiges an Strom sparen. Außerdem wird die Bedienung bei wenig Licht erleichtert. Schließlich empfinden viele einen dunklen Hintergrund als augenschonend. Neu sind dunkle Oberflächen in Android aber nicht. Insbesondere die frühen Versionen enthielten viele schwarze, blaue und graue Elemente.
5.5.1 Das DayNight-Theme
Damit Ihre App das dunkle Design gut unterstützt, sollte sie auf einem DayNight-Theme basieren. Es wird in der Manifestdatei mit dem Attribut android:theme="@style/ ... " festgelegt. Die Definition des Themes befindet sich üblicherweise in der Datei res/values/styles.xml.
<resources>
<style name="AppTheme"
parent="Theme.AppCompat.DayNight.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="myOwnColor">@color/myOwnColor</item>
</style>
</resources>
Mein Beispiel leitet von Theme.AppCompat.DayNight ab. Wenn Sie die Material Components verwenden, können Sie stattdessen Theme.MaterialComponents.DayNight verwenden. Wichtig ist, nach Möglichkeit keine hartkodierten Farben und Icons speziell für helle Designs vorzusehen, sondern vorhandene Theme-Attribute und für den Dark Mode geeignete Ressourcen zu verwenden. textColorPrimary ist eine Allzwecktextfarbe. In hellen Designs erscheint sie fast schwarz, im Dunkelmodus hingegen fast weiß. colorControlNormal ist gut als Allzweckfarbe für Icons geeignet. Wie Sie beide verwenden, ist in Listing 5.46 zu sehen. Die Layoutdatei gehört zu meinem Beispiel DarkModeDemo.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
...
tools:context=".DarkModeDemoActivity">
<TextView
android:id="@+id/textview1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/hello"
android:textColor="?attr/myOwnColor"
android:textSize="16pt"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:contentDescription="@null"
android:src="@drawable/ic_android_black_24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textview1"
app:srcCompat="@android:drawable/sym_def_app_icon"
app:tint="?attr/colorControlNormal" />
<TextView
android:id="@+id/textview2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@string/hello"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16pt"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageview" />
</androidx.constraintlayout.widget.ConstraintLayout>
Die App stellt zweimal den Text »Hallo Android« sowie ein Icon dar. In Abbildung 5.20 sind die Unterschiede bei der Hintergrund- und Iconfarbe sowie dem zweiten Text gut zu erkennen. Auf Papier nicht so schön zu sehen sind hingegen die verschiedenen Farben des ersten Textes. Dessen Farbe wird mit android:textColor="?attr/myOwnColor" gesetzt. Bei aktiviertem Dunkelmodus ist das Grün, sonst Rot.
Die Definition erfolgt in mehreren Stufen. Zuerst wird das Attribut in der Datei attr.xml (Listing 5.47) eingetragen. Sie befindet sich im Verzeichnis res/values. Möglicherweise müssen Sie die Datei erst anlegen. Klicken Sie hierzu im Werkzeugfenster Project auf den Knoten values und wählen dann New • File.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="myOwnColor" format="reference|color" />
</resources>
Attribute haben einen Namen und ein Format. Mit Letzterem legen Sie den Wertebereich und den Typ fest. Nun können wir dem Attribut einen Wert zuweisen. Da es sich um eine Farbe handelt, tragen wir die Definition in die Datei colors.xml ein. Sie wurde vom Assistenten zum Anlegen neuer Projekte unter values abgelegt.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#6200EE</color>
<color name="colorPrimaryDark">#3700B3</color>
<color name="colorAccent">#03DAC5</color>
<color name="myOwnColor">#ff0000</color>
</resources>
In Listing 5.48 setze ich den Wert von myOwnColor auf #ff0000. Das entspricht der Farbe Rot. Damit im Dunkelmodus Grün erscheint, muss es hierfür natürlich ebenfalls eine Definition geben. Wie unter Android üblich, greift hier das Konzept der alternativen Ressourcen. Konkret wird eine zweite Version der Datei colors.xml im Verzeichnis res/values-night abgelegt. Da es nicht automatisch erzeugt wird, müssen Sie es von Hand anlegen. Klicken Sie mit der rechten Maustaste auf den Knoten res und wählen dann File • Directory.
Wenn Sie wie in meinem Beispiel Vektorgrafiken als Icons im Optionsmenü verwenden, können Sie auch deren Farbe an den Dunkelmodus anpassen. Hierzu müssen Sie nur in der .xml-Datei der Grafik das Attribut android:tint="@color/ ... " einfügen (Listing 5.49).
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/myOwnColor"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData=" ... " />
</vector>
5.5.2 Dark Mode in eigenen Themes
Wenn das Theme Ihrer App nicht von DayNight ableitet, können Sie das dunkle Design trotzdem unterstützen. Diese Force Dark genannte Funktion wird mit android:forceDarkAllowed="true" im Theme der App oder Activity aktiviert. Alle hellen System- und AndroidX-Designs, zum Beispiel Theme.Material.Light, setzen es. Bei der Verwendung von dunklen Designs (beispielsweise Theme.Material) wird Force Dark hingegen nicht aktiv. Ist die Funktion eingeschaltet, analysiert Android vor dem Zeichnen jede View und wendet den Dunkelmodus an, sofern das in der Layoutdatei mit android:forceDarkAllowed oder im Code nicht mit forceDarkAllowed = ... deaktiviert wird.