Aufgaben-App: Dark Mode Button

Und weiter geht’s mit unserer Aufgaben-App. Wir wollen auch hier einen Dark Mode einbauen, so ähnlich wie wir das mit der Bumm-App schon gemacht haben, nur diesmal mit einem Button.
Das heißt, klickt man den Button, aktiviert man den Dark Mode. Klickt man den Button erneut, deaktiviert man den Dark Mode wieder. Und die letzte Einstellung soll sich die App merken. Wenn man die App beendet und später wieder neu startet, soll die letzte Einstellung erhalten bleiben.
Download vom Quellcode
Falls du die Aufgaben-App noch nicht kennst, schau dir die bisherigen Videos dazu auf unserem Kanal an. Du kannst dir die App auch hier als Quellcode herunterladen und direkt loslegen den Dark Mode einzubauen: Download der Aufgaben-App
Farben definieren
Zuerst definieren wir die Farben für den Dark Mode. Dazu erstellen wir eine neue colors.xml Datei. Dazu klickst du links in der Projektstruktur auf den Ordner res/values mit der rechten Maustaste und wählst „New“ und dann „Values Resource File“.

Es erscheint ein Dialog. Der Dateiname (File Name) muss colors.xml heißen. In der Liste „available qualifiers“ wählst du Night Mode. Mit dem Pfeil-Button schiebst du die Auswahl in die rechte Liste „chosen qualifiers“. Anschließend stellst du den „Night mode“ auf „Night“ um.

Es wird eine neue Datei erzeugt: colors.xml (night). Die findest du in der Projektstruktur unter res/values/colors. Hier definierst du erneut alle Farben, und zwar so, wie sie im Dark Mode aussehen sollen.

Dann kannst du deine Farben testen. Um den Night Mode in Android zu aktivieren, genügt es den Energiesparmodus auf dem Handy einzuschalten. Ab Android 10 gibt es auch einen separaten Button für den Night Mode in den Quick Settings.
Icon auswählen
Zum Ein- und Ausschalten des Dark-Modes wollen wir ein Icon rechts oben in der Titelleiste platzieren. Die Titelleiste in Android-Apps heißt übrigens „ActionBar“. Das ist ein wichtiger Begriff, den du dir merken solltest, da wir später immer wieder darauf zu sprechen kommen.
Schöne Icons findest du auf materialdesignicons.com. Gib dort einfach einen Suchbegriff ein, zum Beispiel „dark“, und wähle ein geeignetes Icon aus. Du klickst das Icon an und wählst „Advanced Export“. Es öffnet sich ein Fenster mit verschiedenen Einstellungen. Hier kannst du die Größe des Icons wählen. Für ein Icon in der ActionBar empfehle dir 96. Die Farbe des Icons sollte weiß sein. Gib dem Icon einen Dateinamen und klicke dann einfach auf den „Icon“-Button. Die Icon-Datei wird heruntergeladen.

Kopiere sie und füge sie direkt in Android Studio im Ordner „res/mipmap“ ein. Du wirst daraufhin gefragt, in welchem mipmap-Ordner die Datei kopiert werden soll, wähle „mipmap-xxxhdpi“.

Icon in die Titelleiste setzen
Als nächstes setzen wir das Icon rechts oben in die Titelleiste. Dazu müssen wir ein Menü erzeugen. Es nennt sich Menü, wird aber später nicht wie ein Menü aussehen.
Wir erstellen dazu einen neuen Resource-Ordner. Dazu klickst du links in der Projektstruktur auf den Ordner „res“ mit der rechten Maustaste und wählst „New“ und dann „Android Resource Directory“.

Als Directory Name gibst du menu ein. Dann einfach auf OK.

Du findest den neuen menu-Ordner in der Projektstruktur unter „res“. Klicke mit der rechten Maustaste auf den menu-Ordner und dann auf „New“ und auf „Menu Resource File“. So können wir eine neue XML-Datei für unser Menü erzeugen. Als Namen kannst du zum Beispiel main_menu eingeben. Bestätige auch diesen Dialog mit OK.

Es wird eine XML-Datei erzeugt. Schalte am besten wieder in den „Split Mode“ oder den „Text Mode“ um, damit du das XML direkt bearbeiten kannst. Hier müssen wir einen Menü-Eintrag definieren, der XML-Tag dazu heißt „item“. Das sieht so aus:

Dem Item geben wir eine ID, mit der wir später im Java-Code auf das Item zugreifen können, so wie wir das auch immer mit den Views machen. Dann definieren wir einen Titel. Wenn du möchtest, kannst du den Titel direkt als String im XML eingeben, oder über die strings.xml referenzieren. Mit dem Attribut android:icon verknüpfen wir unser heruntergeladenes Icon. Und als letztes ist es noch wichtig mit dem Attribut app:showAsAction festzulegen, dass der Menüpunkt immer nur als Icon dargestellt werden soll. Das sorgt dafür, dass kein richtiges Menü angezeigt wird, sondern eben nur unser Icon. Beachte, dass es app:showAsAction heißt.
Als nächstes müssen wir diese Menü-XML-Datei in unserer MainActivity anbinden. Dazu erstellen wir eine Methode in der MainActivity, die „onCreateOptionsMenu“ heißt. Diese Methode existiert in der AppCompatActivity-Klasse, von der unsere MainActivity erbt. Dort ist sie leer. Diese vorhandene leere Methode überschreiben wir, damit Android weiß, dass wir ein Menü nutzen wollen. Das sieht dann so aus:

Wenn du die App jetzt startest, solltest du das Icon oben rechts in der Titelleiste sehen. Wenn du darauf klickst, passiert aber noch nichts.
Dark Mode Manager
Wir brauchen ein paar Zeilen Code, um den Dark Mode ein- und auszuschalten und den letzten Zustand zu speichern und auch wieder zu laden, wenn die App neugestartet wird. Doch diesen Code wollen wir nicht einfach in unsere MainActivity klatschen. Warum nicht? Ganz einfach: Weil die MainActivity sonst immer größer und unübersichtlicher wird. Außerdem wollen wir Code möglichst wiederverwenden, an anderen Stellen, vielleicht sogar in anderen Apps. Daher empfiehlt es sich, diese ganzen Code-Zeilen zum Dark Mode in eine separate Klasse zu schreiben, die wir dann in der MainActivity einfach benutzen. Wir erstellen dazu einen DarkModeManager.
Rechte Maustaste in der Projektstruktur auf den Package Namen im Java-Ordner, dann auf „New“ und dann auf „Java Class“. Als Namen gibst du DarkModeManager ein. So erzeugen wir eine neue Java-Klasse.

Zunächst erstellen wir zwei Konstanten, eine für Tag-Modus und eine für Nacht-Modus. Konstanten sind im Prinzip sowas wie Variablen, nur dass man sie eben nicht ändern kann. Sie bleiben konstant. Du kannst 1 für Tag nutzen und 2 für Nacht. Oder du machst es so wie ich, indem du deine Konstanten mit den bereits vorhandenen Konstanten verknüpft, die sich Google/Android schon dafür ausgedacht hat. So sieht das aus:

Zum Speichern des letzten Zustands nutzen wir die SharedPreferences-Klasse. Die hatten wir schon mal besprochen, und zwar im Video „Spiele-App Bumm: Teil 4“, als es um die Highscore für die Bumm-App ging. Schau dir das Video dazu an, um mehr zu erfahren. Wir deklarieren also die SharedPreferences als Eigenschaft unseres DarkModeManagers und erstellen einen Konstruktor, der die SharedPreferences initialisiert anhand des Context. Der Context ist in unserer App die MainActivity. Die übergeben wir später von außen beim Instanziieren des DarkModeManagers.
Der Konstruktor sieht so aus:

Zur Zeile setMode(getMode()) kommen wir noch.
Um den Dark Mode ein- bzw. auszuschalten erzeugen wir eine Methode „setMode“. Dieser Methode wird der Modus übergeben, den wir haben wollen, also entweder Tag-Modus oder Nacht-Modus. Um dann tatsächlich den Dark-Mode an- oder auszuschalten nutzen wir eine Android-Klasse, die sich „AppCompatDelegate“ nennt. Die hat eine Methode, um den Dark Mode für die ganzen App umzuschalten (setDefaultNightMode). Außerdem speichern wir in dieser Methode den gewählten Modus in den SharedPreferences.

Um später den zuletzt eingestellten Modus zu ermitteln, empfiehlt es sich auch eine „getMode“-Methode zu haben. Die soll einfach den letzten Zustand aus den SharedPreferences laden und zurückgeben.

Damit wir einfach zwischen Tag- und Nacht-Modus umschalten können, schreiben wir zu guter letzt noch eine „toggle“-Methode. Die nimmt immer den letzten Zustand und schaltet anhand dieser Information in den entsprechenden anderen Zustand um.

Der Aufruf in der toggle-Methode sieht etwas kompliziert aus, ist aber eigentlich ganz einfach. Wir lesen von innen nach außen, um den Aufruf zu verstehen. Zuerst getMode(), um den letzten Zustand zu laden. Dann prüfen, ob der letzte Zustand der Tag-Modus war. Falls ja, soll der Nacht-Modus der gewünschte Zustand sein. Ansonsten der Tag-Modus. Und das ganze steht in setMode(…), damit der gewünschte Zustand dann aktiviert und wieder gespeichert wird.
Dark Mode an- und ausschalten
Den DarkModeManager nutzen wir in der MainActivity, denn die MainActivity ist unser Controller, das heißt unsere Steuerung für alles, was in der App passieren soll.
Dazu erzeugen wir eine neue Methode in der MainActivity. Genau genommen überschreiben wir wieder eine Methode, die bereits in der AppCompatActivity vorhanden ist, von der wir ja erben. Diese Methode heißt „onOptionsItemSelected“. Diese Methode wird immer dann aufgerufen, wenn irgend ein Menüpunkt, also irgend ein Icon in der oberen Titelleiste (ActionBar) aufgerufen wird.

Da wir theoretisch mehrere Icons in der ActionBar haben könnten und später vielleicht auch haben werden, prüfen wir, ob der Nutzer auch tatsächlich auf das Icon für den Dark-Mode geklickt hat. Dazu vergleichen wir die ID des MenuItem, das der Methode übergeben wird, mit der ID des MenüItem vom Dark-Mode. Stimmen beide überein, rufen wir einfach die toggle-Methode vom DarkModeManager auf, die wir vorhin geschrieben haben. Die kümmert sich um den Rest. Der Dark Mode wird entsprechend umgeschaltet und der neue Zustand gespeichert.
That’s it! 🙂