Ohl*_*sen 7 kotlin android-jetpack-compose android-jetpack-datastore kotlin-stateflow
我尝试让用户选择是否使用 UI 模式“浅色”、“深色”或“系统”设置。我想将选择保存为数据存储。
用户选择的下拉菜单未从数据存储加载值。加载屏幕时,它始终显示 stateIn() 的初始值。
设置管理器:
val Context.dataStoreUiSettings: DataStore<Preferences> by preferencesDataStore(name = DATA_STORE_UI_NAME)
object PreferencesKeys {
val UI_MODE: Preferences.Key<Int> = intPreferencesKey("ui_mode")
}
class SettingsManager @Inject constructor(private val context: Context) { //private val dataStore: DataStore<Preferences>
private val TAG: String = "UserPreferencesRepo"
// Configuration.UI_MODE_NIGHT_UNDEFINED, Configuration.UI_MODE_NIGHT_YES, Configuration.UI_MODE_NIGHT_NO
suspend fun setUiMode(uiMode: Int) {
context.dataStoreUiSettings.edit { preferences ->
preferences[UI_MODE] = uiMode
}
}
fun getUiMode(key: Preferences.Key<Int>, default: Int): Flow<Int> {
return context.dataStoreUiSettings.data
.catch { exception ->
if (exception is IOException) {
Timber.i("Error reading preferences: $exception")
emit(emptyPreferences())
} else {
throw exception
}
}
.map { preference ->
preference[key] ?: default
}
}
fun <T> getDataStore(key: Preferences.Key<T>, default: Any): Flow<Any> {
return context.dataStoreUiSettings.data
.catch { exception ->
if (exception is IOException) {
Timber.i("Error reading preferences: $exception")
emit(emptyPreferences())
} else {
throw exception
}
}
.map { preference ->
preference[key] ?: default
}
}
suspend fun clearDataStore() {
context.dataStoreUiSettings.edit { preferences ->
preferences.clear()
}
}
suspend fun removeKeyFromDataStore(key: Preferences.Key<Any>) {
context.dataStoreUiSettings.edit { preference ->
preference.remove(key)
}
}
}
Run Code Online (Sandbox Code Playgroud)
视图模型:
@HiltViewModel
class SettingsViewModel @Inject constructor(
private val settingsUseCases: SettingsUseCases,
private val settingsManager: SettingsManager,
) : ViewModel() {
private val _selectableUiModes = mapOf(
UI_MODE_NIGHT_UNDEFINED to "System",
UI_MODE_NIGHT_NO to "Light",
UI_MODE_NIGHT_YES to "Dark"
)
val selectableUiModes = _selectableUiModes
val currentUiMode: StateFlow<Int?> = settingsManager.getUiMode(UI_MODE, UI_MODE_NIGHT_UNDEFINED).stateIn(
scope = viewModelScope,
started = WhileSubscribed(5000),
initialValue = null,
)
init {
Timber.i("SettingsViewModel created")
}
fun setUiMode(uiModeKey: Int) {
viewModelScope.launch(Dispatchers.IO) {
settingsManager.setUiMode(uiModeKey)
}
}
}
Run Code Online (Sandbox Code Playgroud)
撰写:
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun OutlinedDropDown(
modifier: Modifier = Modifier,
readOnly: Boolean = true,
isEnabled: Boolean = true,
isError: Boolean = false,
settingsViewModel: SettingsViewModel = hiltViewModel(),
) {
val items: Map<Int, String> = settingsViewModel.selectableUiModes
var expanded by remember { mutableStateOf(false) }
val selectedItemIndex = settingsViewModel.currentUiMode
var selectedText by remember { mutableStateOf(if (selectedItemIndex.value == null) "" else items[selectedItemIndex.value]) }
val optionList by remember { mutableStateOf(items) }
Column {
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = {
expanded = !expanded
}
) {
OutlinedTextField(
isError = isError,
enabled = isEnabled,
modifier = modifier,
readOnly = readOnly,
value = selectedText!!,
onValueChange = {
selectedText = it
},
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(
expanded = expanded
)
}
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = {
expanded = false
}
) {
optionList.forEach { selectionOption ->
DropdownMenuItem(
onClick = {
selectedText = selectionOption.value
settingsViewModel.setUiMode(selectionOption.key)
expanded = false
}
) {
Text(text = selectionOption.value)
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
为什么 currentUiMode 的值没有更新?我不想使用 LiveData。
如果有人在寻找 Compose DataStore 包装器时找到了这个答案,请查看这个答案。
在 Compose 中唯一可能导致重组的事情是更改对象State。
简单地发送到流并不能做到这一点。您可以使用 收集流量,它是tocollectAsState的映射器。With你需要一个默认值,因为它没有 current ,但你不需要它。FlowStateFlowvalueStateFlow
代码中的另一个问题是,remember { mutableStateOf...仅记住第一个值,selectedText不会使用selectedItemIndex. 一般来说,您可以将其作为参数传递给remember, 或 use derivedStateOf,但在这种特殊情况下,根本不需要使用remember& mutableStateOf,就像 的情况一样optionList,因为这些是静态值,selectedItemIndex不会经常更新。
remembermutableStateOf仅当您需要更改某些具有副作用的值(例如单击按钮)时才应使用& 。请参阅此答案了解其工作原理。如果您不想重复中等严重程度的计算,也可以使用rememberwithout - 不要在没有副作用或后台线程的情况下进行真正繁重的计算。mutableStateOf
因此,以下内容应该适合您:
var expanded by remember { mutableStateOf(false) }
val selectedItemIndex by settingsViewModel.currentUiMode.collectAsState()
var selectedText = if (selectedItemIndex == null) "" else items[selectedItemIndex]
val optionList: Map<Int, String> = settingsViewModel.selectableUiModes
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3977 次 |
| 最近记录: |