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)
    }
}
Другие вопросы по тегам