tos*_*gic 11 kotlin android-jetpack-compose kotlin-flow kotlin-stateflow
我正在尝试让显示缩放功能与 JetPack Compose 一起使用。我有一个 ViewModel 将共享首选项值公开为流,但这绝对是不正确的,如下所示:
@HiltViewModel
class MyViewModel @Inject constructor(
@ApplicationContext private val context: Context
) : ViewModel() {
private val _densityFactor: MutableStateFlow<Float> = MutableStateFlow(1.0f)
val densityFactor: StateFlow<Float>
get() = _densityFactor.asStateFlow()
private fun getDensityFactorFromSharedPrefs(): Float {
val sharedPreference = context.getSharedPreferences(
"MY_PREFS",
Context.MODE_PRIVATE
)
return sharedPreference.getFloat("density", 1.0f)
}
// This is what I look at and go, "this is really bad."
private fun densityFactorFlow(): Flow<Float> = flow {
while (true) {
emit(getDensityFactorFromSharedPrefs())
}
}
init {
viewModelScope.launch(Dispatchers.IO) {
densityFactorFlow().collectLatest {
_densityFactor.emit(it)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的可组合项:
@Composable
fun MyPageRoot(
modifier: Modifier = Modifier,
viewModel: MyViewModel = hiltViewModel()
) {
val densityFactor by viewModel.densityFactor.collectAsState(initial = 1.0f)
CompositionLocalProvider(
LocalDensity provides Density(
density = LocalDensity.current.density * densityFactor
)
) {
// Content
}
}
Run Code Online (Sandbox Code Playgroud)
这里有一个滑块,我想用手指滑动它来设置显示缩放比例(滑块位于 MyPageRoot 的内容之外,并且当用户使用滑块时不会更改屏幕上的大小)。
@Composable
fun ScreenDensitySetting(
modifier: Modifier = Modifier,
viewModel: SliderViewModel = hiltViewModel()
) {
var sliderValue by remember { mutableStateOf(viewModel.getDensityFactorFromSharedPrefs()) }
Text(
text = "Zoom"
)
Slider(
value = sliderValue,
onValueChange = { sliderValue = it },
onValueChangeFinished = { viewModel.setDisplayDensity(sliderValue) },
enabled = true,
valueRange = 0.5f..2.0f,
steps = 5,
colors = SliderDefaults.colors(
thumbColor = MaterialTheme.colors.secondary,
activeTrackColor = MaterialTheme.colors.secondary
)
)
}
Run Code Online (Sandbox Code Playgroud)
滑块可组合项有自己的视图模型
@HiltViewModel
class PersonalizationMenuViewModel @Inject constructor(
@ApplicationContext private val context: Context
) : ViewModel() {
fun getDensityFactorFromSharedPrefs(): Float {
val sharedPreference = context.getSharedPreferences(
"MY_PREFS",
Context.MODE_PRIVATE
)
return sharedPreference.getFloat("density", 1.0f)
}
fun setDisplayDensity(density: Float) {
viewModelScope.launch {
val sharedPreference = context.getSharedPreferences(
"MEAL_ASSEMBLY_PREFS",
Context.MODE_PRIVATE
)
val editor = sharedPreference.edit()
editor.putFloat("density", density)
editor.apply()
}
}
}
Run Code Online (Sandbox Code Playgroud)
我知道我需要将所有共享首选项代码移至一个类中。但是我该如何编写流程,以便在值发生变化时从共享首选项中提取?我觉得我需要某种监听器,但对 Android 开发来说非常陌生。
Ten*_*r04 18
你的评论是对的,这确实很糟糕。:) 您应该创建一个 OnSharedPreferenceChangeListener,以便它对更改做出反应,而不是锁定 CPU 来不断地先发制人地检查它。
用于callbackFlow将听众转换为流程。你可以这样使用它:
fun SharedPreferences.getFloatFlowForKey(keyForFloat: String) = callbackFlow<Float> {
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
if (keyForFloat == key) {
trySend(getFloat(key, 0f))
}
}
registerOnSharedPreferenceChangeListener(listener)
if (contains(keyForFloat)) {
send(getFloat(keyForFloat, 0f)) // if you want to emit an initial pre-existing value
}
awaitClose { unregisterOnSharedPreferenceChangeListener(listener) }
}.buffer(Channel.UNLIMITED) // so trySend never fails
Run Code Online (Sandbox Code Playgroud)
然后你的 ViewModel 变成:
@HiltViewModel
class MyViewModel @Inject constructor(
@ApplicationContext private val context: Context
) : ViewModel() {
private val sharedPreference = context.getSharedPreferences(
"MY_PREFS",
Context.MODE_PRIVATE
)
val densityFactor: StateFlow<Float> = sharedPreferences
.getFloatFlowForKey("density")
.stateIn(viewModelScope, SharingStarted.Eagerly, 1.0f)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2500 次 |
| 最近记录: |