ContextCompat.getColor() игнорировать NightMode
TL,DR;
ContextCompat.getColor()
не использует ночные цвета (values-night/colors.xml
) хотя должно, когда включен ночной режим.
Вот в чем проблема:
Привет всем,
Итак, я реализую темную тему для своего Android-приложения, я вызываю это, чтобы включить ее:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
Я установил цвета в values/colors.xml
и есть темная версия в values-night/colors.xml
. Цвета хорошо меняются в зависимости от nightMode, НО:
когда я использую ContextCompat.getColor(getApplicationContext(), R.id.myColor)
, используются обычные цвета (values/colors.xml
) а не ночные краски (values-night/colors.xml
).
В моем build.gradle
, Я установил эти:
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0-beta01'
Может ли кто-нибудь сказать мне, что я делаю не так?
PS: я уже смотрел на следующий вопрос, и он не отвечает на эту проблему /questions/50885206/metod-contextcompatgetcolor-ignoriruet-nochnoj-rezhim
4 ответа
Я столкнулся с аналогичными проблемами с ночным режимом. Некоторые экраны были в порядке, но другие сохранили обычную тему. В конце концов, я обнаружил, что создавал экземпляры некоторых представлений, используя контекст приложения вместо текущего контекста активности. По какой-то причине контекст приложения не отслеживает такую информацию.
Итак, обновите свой код, чтобы использовать контекст текущей активности вместо контекста приложения.
Для справки для других пользователей. Избегайте:
ContextCompat.getColor(getApplicationContext(), R.id.myColor)
И используйте:
// 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 getColor can trigger a NPE
Context context = getContext()
if (context != null) {
ContextCompat.getColor(context, R.id.myColor)
}
Контекст приложения ничего не знает о текущей теме или дне/ночи, поэтому, если вы получаете ресурс из контекста приложения, вы получите ресурс в теме приложения по умолчанию. Решением этой проблемы является использование контекста активности/фрагмента, но в некоторых ситуациях у вас может не быть активности или фрагмента, а только контекст приложения. Я создал класс оболочки контекста, чтобы добавить тему «День и ночь» в контекст приложения:
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...
}
Этот код был протестирован и работает.
Я столкнулся с таким же вопросом, как и вы, и я также обнаружил, что суть проблемы заключается в том, что у приложения нет оболочки темы, такой как Activity. поэтому ответ W0rmH0le может решить эту проблему. но для меня есть много кода, который не может получить контекст действия или представления. поэтому я подумал, почему бы нам не создать одну тему ночного режима для одноэлементной контекстной оболочки. есть код, и он отлично работает для меня.
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);
тогда вы можете использовать mThemeContext вместо приложения, и вы сможете найти правильный цвет в ночном режиме.
Для тех, кто найдет его позже. Я немного подправил решение @ygngy
Этот ответ решает проблему конфигурации во время выполнения и оставляет фактический контекст при каждом вызове. Его также можно было использовать с кинжалом, чтобы каждый раз не генерировать миллион объектов.
в некоторых случаях это необходимо, когда цвет нужно брать во время выполнения, например в маппере при получении данных с сервера и так далее
interface ThemeContextProvider {
fun getColor(@ColorRes id: Int): Int
fun getDrawable(@DrawableRes id: Int): Drawable?
}
class ThemeContextProviderImpl @Inject constructor(
private val application: Context
) : ThemedContext {
private val res: Resources = application.resources
private val filter by lazy {
application.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK.inv()
}
private lateinit var themedContext: Context
private var configuration = Configuration(res.configuration)
init {
getCurrentContext()
}
private fun getCurrentContext() {
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)
}
override fun getColor(@ColorRes id: Int): Int {
getCurrentContext()
return ContextCompat.getColor(themedContext, id)
}
override fun getDrawable(@DrawableRes id: Int): Drawable? {
getCurrentContext()
return ContextCompat.getDrawable(themedContext, id)
}
}