#android #android-theme #kotlin-coroutines #android-jetpack-datastore
Вопрос:
Я переключил свое приложение с использования SharedPreferences
на PreferencesDataStore
. Я также реализовал темный режим и несколько тем внутри своего приложения. Для тематизации я в основном полагаюсь в каждом действии на этот код:
val themePreferences = getSharedPreferences("THEME_PREFERENCES", MODE_PRIVATE)
val settingsPreferences = getSharedPreferences("SETTINGS_PREFERENCES", MODE_PRIVATE)
darkMode = settingsPreferences.getBoolean("darkMode", false)
setTheme(when (darkMode) {
true -> themePreferences.getInt("themeDark", R.style.AppThemeDark)
false -> themePreferences.getInt("themeLight", R.style.AppThemeLight)
})
Выбранная тема сохраняется в виде целого числа, каждый раз для светлого режима, а также для темного режима. Теперь я также хочу принять этот раздел моего кодекса. Я получаю логическое значение darkMode из своего хранилища данных следующим образом:
viewModel.storedDarkMode.observe(this) { darkMode = it }
Если я буду работать внутри наблюдения(этого) { … } объекта LiveData это не сработает.
Теперь, как я могу изменить фрагмент кода, показанный выше, на PreferencesDataStore
? Или на самом деле лучше, например, создать отдельный класс, чтобы наблюдать значения оттуда? Если да, то как может выглядеть нечто подобное? Или вы знаете какой-нибудь хороший пример кода, следующего архитектуре Android, включая пользовательские темы с темным режимом, на которые я могу посмотреть?
Я все еще многому учусь, любая помощь для лучшего понимания этого очень ценится!
С наилучшими пожеланиями, Маркус
Редактировать:
runBlocking {
val darkMode = viewModel.darkModeFlow.first()
setTheme(when (darkMode) {
true -> viewModel.themeDarkFlow.first()
false -> viewModel.themeLightFlow.first()
})
}
Ответ №1:
Я не знаю, является ли это лучшей практикой или нет, но я использую runBlocking
для получения данных о теме dataStore
. Всегда рекомендуется, чтобы мы никогда не использовали runBlocking
в нашем производственном коде.
val preferences = runBlocking {
mainActivityViewModel.preferencesFlow.first()
}
setAppTheme(preferences.currentTheme)
binding = ActivityMainBinding.inflate(layoutInflater)
В setAppTheme
способе
private fun setAppTheme(theme: Boolean) {
mainActivityViewModel.darkMode = theme
//set your theme here
}
Теперь понаблюдайте preferencesFlow
за любым изменением значения темы, и если тема будет изменена, то recreate()
mainActivityViewModel.preferencesLiveData.observe(this) {
if (it.currentTheme != mainActivityViewModel.selectedTheme) {
recreate()
}
}
Поскольку мы не можем загрузить наш пользовательский интерфейс, не получив тему. Это казалось правильным в использовании runBlocking
.
Комментарии:
1. Спасибо за ваш ответ! Поскольку блокировка запуска затем замораживает поток пользовательского интерфейса, есть ли у вас какие-либо проблемы с этим подходом?
2. Даже ни одного.
3. Честно говоря, мне не очень нравится использование блокировки запуска, но она отлично работает. Видите ли вы какие-либо проблемы с кодом, который я отредактировал ниже своего вопроса?
4. Правка: Еще одна вещь, это решение не будет работать для меня до тех пор, пока не изменится само значение потока. Что мне нужно было бы сделать, так это воссоздать также саму модель представления, но это невозможно, что, если бы я видел до сих пор. Что мне любопытно в данный момент, так это то, что, когда я воссоздаю свою Деятельность, иногда Поток приспосабливается к изменениям, но часто нет. В то время как когда я полностью перезапускаю его, он работает так, как задумано.
5. Вы должны вывести
setTheme
вызов метода изrunBlocking
, Почему вы используете два разныхflows
для хранения одного и того же значения и для хранения текущей темы, вам не нужно иметьflow
, вы должны использовать обычнуюboolean
переменную. Вы должны наблюдатьliveData
dataStore
и сравнивать сохраненное значение темыviewModel
с последним значением, котороеdataStore
дают текущие данные, если они не совпадаютrecreate()
. (Я по ошибке ввел поток в коде своего ответа)