Implement Dark/Night Mode in an Android app with a ListPreference toggle

Cover Photo by Maxim Ilyahov on Unsplash

Hello, friends!

To introduce myself, I am Neeyat Lotlikar, an Android Developer. I am currently working on my portfolio and I’d like to share what I’ve learned recently while working on it. This is my first post so feel free to make any corrections and suggestions.

Without any further delay, let’s get going!

The first step to enable dark mode is to change your app theme to Theme.AppCompat.DayNight or Theme.MaterialComponents.DayNight if you’re using the Material Design library.

res/values/styles.xml

<style name="AppTheme" parent="Theme.AppCompat.DayNight">
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

Enter fullscreen mode Exit fullscreen mode

Next, create a Preference activity.
This can be done with the help of the Android Studio templates with ease or you can even do it manually.

SettingsActivity.kt

class SettingsActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.settings_activity)
        supportFragmentManager
            .beginTransaction()
            .replace(R.id.settings, SettingsFragment())
            .commit()
    }

    class SettingsFragment : PreferenceFragmentCompat() {
        override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
            setPreferencesFromResource(R.xml.root_preferences, rootKey)
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

We want a ListPreference in our root_prefernces.xml file and so it should look something like this:

res/values/xml/root_preferences.xml

<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">

    <PreferenceCategory app:title="@string/ui">

        <ListPreference
            app:defaultValue="@string/dark_mode_def_value"
            app:entries="@array/dark_mode_entries"
            app:entryValues="@array/dark_mode_values"
            app:key="@string/dark_mode"
            app:title="@string/dark_mode" />

    </PreferenceCategory>

</PreferenceScreen>

Enter fullscreen mode Exit fullscreen mode

Here, the app:defaultValue is used to set a default value which is stored in strings.xml. The app:title and app:key attribute values specify the display title of the preference and the key to identify it by respectively.

res/values/strings.xml

<resources>
    <!-- Preference Titles -->
    <string name="ui">UI</string>
    <string name="dark_mode">Dark Mode</string>
    <string name="dark_mode_def_value">MODE_NIGHT_FOLLOW_SYSTEM</string>
</resources>

Enter fullscreen mode Exit fullscreen mode

app:entries and app:entryValues attributes take array values that specify the corresponding display name and the value it carries so make sure they are in the proper order. I have added my arrays to arrays.xml as below:

res/values/arrays.xml

<resources>
    <!-- Dark Mode ListPreference -->
    <string-array name="dark_mode_entries">
        <item>Follow System Dark Mode</item>
        <item>Light Mode Selected</item>
        <item>Dark Mode Selected</item>
        <item>Auto Battery Dark Mode</item>
    </string-array>

    <string-array name="dark_mode_values">
        <item>MODE_NIGHT_FOLLOW_SYSTEM</item>
        <item>MODE_NIGHT_NO</item>
        <item>MODE_NIGHT_YES</item>
        <item>MODE_NIGHT_AUTO_BATTERY</item>
    </string-array>

</resources>

Enter fullscreen mode Exit fullscreen mode

Now that we are done with the layout, let’s look at the code.

The AppCompatDelegate class’ setDefaultNightMode() function is used to change the night mode settings. Following values can be passed to this method:
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM – This will toggle the night mode based on whether the system-wide dark mode is enabled or not.
AppCompatDelegate.MODE_NIGHT_NO – Light mode will be enabled.
AppCompatDelegate.MODE_NIGHT_YES – Dark/Night mode will be enabled.
AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY – Dark/Night mode will be enabled when the device is on the battery saving mode.

So where do we use this method?
Implement SharedPreferences.OnSharedPreferenceChangeListener in SettingsActivity and override the onSharedPreferenceChanged() function.

SettingsActivity.kt

override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
        val darkModeString = getString(R.string.dark_mode)
        key?.let {
            if (it == darkModeString) sharedPreferences?.let { pref ->
                val darkModeValues = resources.getStringArray(R.array.dark_mode_values)
                when (pref.getString(darkModeString, darkModeValues[0])) {
                    darkModeValues[0] -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
                    darkModeValues[1] -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
                    darkModeValues[2] -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
                    darkModeValues[3] -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY)
                }
            }
        }
    }

Enter fullscreen mode Exit fullscreen mode

As you can see, I retrieve the preference in the above code and use it to determine which option is selected in the list preference. Using this, the night mode settings are changed.

Now, a very important thing to do is registering and unregistering the OnSharedPreferenceChangeListener. This will ensure that the changes are made almost instantly on user input and it also prevents memory leakage.
Register the listener in the onCreate() function and unregister it in the onDestroy() function.

SettingsActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.settings_activity)
    supportFragmentManager
        .beginTransaction()
        .replace(R.id.settings, SettingsFragment())
        .commit()
    supportActionBar?.setDisplayHomeAsUpEnabled(true)

    PreferenceManager.getDefaultSharedPreferences(this)
        .registerOnSharedPreferenceChangeListener(this)
}

override fun onDestroy() {
    super.onDestroy()
    PreferenceManager.getDefaultSharedPreferences(this)
        .unregisterOnSharedPreferenceChangeListener(this)
}

Enter fullscreen mode Exit fullscreen mode

Now, a provision has to be made to make navigation to the SettingsActivity possible which I did by adding an action to the options menu. You can use whatever works best for you.

It is possible to add separate icons, colors, styles, and other resources to be used in the day and the night mode. You do so by adding these resources in resource folders with the extension of -night. For example, drawable-night or values-night.

And with this, we’ve successfully got our dark mode toggle working.

There’s a problem, however. As you can see, the ListPreference doesn’t show which option is currently selected in the summary. How do we fix that? Well, that’s very simple actually. There are just two things that need to be done here:
Firstly, set app:useSimpleSummaryProvider attribute value to true in ListPreference.

res/xml/root_preferences.xml

<ListPreference
    app:defaultValue="@string/dark_mode_def_value"
    app:entries="@array/dark_mode_entries"
    app:entryValues="@array/dark_mode_values"
    app:key="@string/dark_mode"
    app:title="@string/dark_mode"
    app:useSimpleSummaryProvider="true" />

Enter fullscreen mode Exit fullscreen mode

Secondly, implement the Preference.SummaryProvider<ListPreference> interface in SettingsActivity and override it’s provideSummary() function.

SettingsActivity.kt

override fun provideSummary(preference: ListPreference?): CharSequence =
        if (preference?.key == getString(R.string.dark_mode)) preference.entry
        else "Unknown Preference"

Enter fullscreen mode Exit fullscreen mode

Note that I am using androidx.preference.Preference. SummaryProvider is not available in android.preference.Preference i.e. the android package.

And with this, this tutorial is completed.
Check out the full code on my GitHub here:

neeyatl / DarkModePreferencesTutorial

A tutorial app as a guide for implementing Night/Dark Mode using ListPreference in Android.

DarkModePreferencesTutorial

A tutorial app as a guide for implementing Night/Dark Mode using ListPreference in Android.

This tutorial blog teaches how to change the app theme using the androidx.appcompat.app.AppCompatDelegate class. It also teaches how to use ListPreference from the androidx.preference package to create a switch in the in-app settings (activity) to allow the user to choose the setting that she/he prefers.

Watch the app in action

See the full tutorial blog post: https://dev.to/aurumtechie/implement-dark-night-mode-in-an-android-app-with-a-listpreference-toggle-2k5i

This is the Kotlin implementation of this app. For it’s java version, visit: https://github.com/Aurum1611/DarkModePreferenceTutorialJava

View on GitHub

Upon request, I’ve created a Java counterpart for the project:

图片[1]-Implement Dark/Night Mode in an Android app with a ListPreference toggle - 拾光赋-拾光赋

neeyatl
/
DarkModePreferenceTutorialJava

Part of a tutorial that shows how to create Dark Mode in Android. This project is written in Java and is a counterpart to DarkModePreferenceTutorial, the Kotlin version.

DarkModePreferenceTutorialJava

A tutorial app as a guide for implementing Night/Dark Mode using ListPreference in Android.

This tutorial blog teaches how to change the app theme using the androidx.appcompat.app.AppCompatDelegate class. It also teaches how to use ListPreference from the androidx.preference package to create a switch in the in-app settings (activity) to allow the user to choose the setting that she/he prefers.

Watch the app in action

See the full tutorial blog post: https://dev.to/aurumtechie/implement-dark-night-mode-in-an-android-app-with-a-listpreference-toggle-2k5i

This is the Java implementation of this app. For it’s Kotlin version, visit: https://github.com/Aurum1611/DarkModePreferencesTutorial

View on GitHub

I hope this is helpful. Happy coding!

原文链接:Implement Dark/Night Mode in an Android app with a ListPreference toggle

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容