TL,DR;
ContextCompat.getColor()
does not use the night colors (values-night/colors.xml
) though it should when night mode is enabled.
Here is the problem:
Hi everyone,
So I'm implementing a dark theme for my Android app, I call this to enable it :
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
I have set colors in values/colors.xml
and there dark version in values-night/colors.xml
. The colors changes well depending on the nightMode, BUT :
when I use ContextCompat.getColor(getApplicationContext(), R.id.myColor)
, it uses the normal colors (values/colors.xml
) and not the night colors (values-night/colors.xml
).
In my build.gradle
, I have set these :
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0-beta01'
Could someone please tell me what am I doing wrong ?
PS : I already looked at the following question and it does not answer this problem https://stackoverflow.com/questions/57779661/contextcompat-getcolor-method-ignores-night-mode
I faced similar issues with night mode. Some screens were fine but others were keeping the regular theme. In the end, I found out that I was instantiating some views using the Application's context instead of the current's activity context. For some reason, Application's context does not track this kind of information.
So, update your code to use current's activity context instead of the application context.
For reference for other users. Avoid:
ContextCompat.getColor(getApplicationContext(), R.id.myColor)
And use:
// In a Activity
ContextCompat.getColor(this, R.id.myColor)
// In a View
ContextCompat.getColor(getContext(), R.id.myColor)
// In a Fragment (check against null because getContext can trigger a NPE
Context context = getContext()
if (context != null) {
ContextCompat.getColor(context, R.id.myColor)
}
I faced similar question as you, and I also found the core of the issue is that Application don't have a theme wrapper like Activity. so W0rmH0le's answer can resolve this problem. but for me, there are lots of code can't fetch an activity or view's context. so I thought why don't we create one singleton Context wrapper nightMode theme. there is the code, and it works fine for me.
Resources res = getApplication().getResources();
Configuration configuration = new Configuration(res.getConfiguration());
int nightNode = AppCompatDelegate.getDefaultNightMode();
if (nightNode == AppCompatDelegate.MODE_NIGHT_NO) {
configuration.uiMode = Configuration.UI_MODE_NIGHT_NO | (res.getConfiguration().uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
} else if (nightNode == AppCompatDelegate.MODE_NIGHT_YES) {
configuration.uiMode = Configuration.UI_MODE_NIGHT_YES| (res.getConfiguration().uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
} else {
configuration.uiMode = res.getConfiguration().uiMode;
}
mThemeContext = getApplication().createConfigurationContext(configuration);
then you can use mThemeContext replace Application, and you can found the right color followed night mode.
Application context don't know anything about current theme or day/night so if you get a resource from application context, you will get the resource in default app theme. A solution to this problem is to use activity/fragment context but in some situations you may not have activity or fragment and only have application context. I have created a context wrapper class to add Day & Night theme to Application context:
import android.content.Context
import android.content.res.Configuration
import android.content.res.Resources
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.ContextCompat
class ThemedContext(application: Context) {
private val themedContext: Context
init {
val res: Resources = application.resources
val configuration = Configuration(res.configuration)
val filter = res.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK.inv()
configuration.uiMode = when (AppCompatDelegate.getDefaultNightMode()) {
AppCompatDelegate.MODE_NIGHT_NO -> Configuration.UI_MODE_NIGHT_NO or filter
AppCompatDelegate.MODE_NIGHT_YES -> Configuration.UI_MODE_NIGHT_YES or filter
else -> res.configuration.uiMode
}
themedContext = application.createConfigurationContext(configuration)
}
fun getColor(@ColorRes id: Int) = ContextCompat.getColor(themedContext, id)
fun getDrawable(@DrawableRes id: Int) = ContextCompat.getDrawable(themedContext, id)
//todo Add other getter methods as needed...
}
This code has been tested and worked.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With