Android Jetpack Compose (Composable) 从Coroutine获取String资源
Android Jetpack Compose (Composable) Get String resources from Coroutine
我有一个 Flow 封装了一个密封的 class,用于将消息调用到 UI。例如显示一个 Snackbar。我可以使用 LaunchedEffect 从 Composable 观察 Flow。但问题是我想从协程范围中检索字符串资源。而且我不想在我的视图模型中使用直接字符串值,因为我想为不同的语言使用 Locale,所以我传递了 String Res Id。那么有没有一种很好的方法可以做到这一点,而无需将字符串资源放入应用程序 class 并从此处检索它们 getString Outside of a Context or Activity
代码如下:
- 我有用于显示 snackbar 的脚手架,当用户单击填满整个屏幕的脚手架时,将调用 showSnackbarWithScope 方法,它会向 Flow 发出新值。
- 然后通过LaunchedEffect捕捉变化
- 然后它用消息调用 showSnackbar() 方法,但该函数需要 String 而我有 String Res Id (Int) 值
- 我无法使用从可组合范围检索字符串的 stringResource() 方法的问题,因为我在协程中
@Composable
fun HomeScreen(viewModel: CityWeatherViewModel) {
val scaffoldState = rememberScaffoldState()
LaunchedEffect(true) {
viewModel.eventFlow.collectLatest { event ->
when (event) {
is CityWeatherViewModel.UIEvent.ShowSnackbar -> {
// THE PROBLEM IS HERE!!!, since event.messageResId is res id, and not the string resource, and I can not use stringResource from coroutine
scaffoldState.snackbarHostState.showSnackbar(
message = event.messageResId
)
}
}
}
}
Scaffold(
scaffoldState = scaffoldState,
modifier = Modifier
.fillMaxSize()
.clickable {
viewModel.showSnackbarWithScope(R.string.could_not_determine_location)
}
) {
}
}
@HiltViewModel
class CityWeatherViewModel @Inject constructor(
private val getCityWeather: GetCityWeather
) : ViewModel() {
// to show snackbar with error
private val _eventFlow = MutableSharedFlow<UIEvent>()
val eventFlow = _eventFlow.asSharedFlow()
suspend fun showSnackbar(stringResId: Int) {
_eventFlow.emit(
UIEvent.ShowSnackbar(stringResId)
)
}
fun showSnackbarWithScope(stringResId: Int) {
viewModelScope.launch {
showSnackbar(stringResId)
}
_eventFlowSwitch.value = !_eventFlowSwitch.value
}
sealed class UIEvent {
data class ShowSnackbar(val messageResId: Int) : UIEvent()
}
companion object {
// time in ms, before we request new data
const val DELAY_BEFORE_REQUEST = 1500L
}
}
您可以使用 LocalContext
在任何可组合项中获取 Context
,然后在 LaunchedEffect
中使用它来获取您的资源:
val context = LocalContext.current
LaunchedEffect(true) {
context.resources.getString(event.messageResId)
}
我有一个 Flow 封装了一个密封的 class,用于将消息调用到 UI。例如显示一个 Snackbar。我可以使用 LaunchedEffect 从 Composable 观察 Flow。但问题是我想从协程范围中检索字符串资源。而且我不想在我的视图模型中使用直接字符串值,因为我想为不同的语言使用 Locale,所以我传递了 String Res Id。那么有没有一种很好的方法可以做到这一点,而无需将字符串资源放入应用程序 class 并从此处检索它们 getString Outside of a Context or Activity
代码如下:
- 我有用于显示 snackbar 的脚手架,当用户单击填满整个屏幕的脚手架时,将调用 showSnackbarWithScope 方法,它会向 Flow 发出新值。
- 然后通过LaunchedEffect捕捉变化
- 然后它用消息调用 showSnackbar() 方法,但该函数需要 String 而我有 String Res Id (Int) 值
- 我无法使用从可组合范围检索字符串的 stringResource() 方法的问题,因为我在协程中
@Composable
fun HomeScreen(viewModel: CityWeatherViewModel) {
val scaffoldState = rememberScaffoldState()
LaunchedEffect(true) {
viewModel.eventFlow.collectLatest { event ->
when (event) {
is CityWeatherViewModel.UIEvent.ShowSnackbar -> {
// THE PROBLEM IS HERE!!!, since event.messageResId is res id, and not the string resource, and I can not use stringResource from coroutine
scaffoldState.snackbarHostState.showSnackbar(
message = event.messageResId
)
}
}
}
}
Scaffold(
scaffoldState = scaffoldState,
modifier = Modifier
.fillMaxSize()
.clickable {
viewModel.showSnackbarWithScope(R.string.could_not_determine_location)
}
) {
}
}
@HiltViewModel
class CityWeatherViewModel @Inject constructor(
private val getCityWeather: GetCityWeather
) : ViewModel() {
// to show snackbar with error
private val _eventFlow = MutableSharedFlow<UIEvent>()
val eventFlow = _eventFlow.asSharedFlow()
suspend fun showSnackbar(stringResId: Int) {
_eventFlow.emit(
UIEvent.ShowSnackbar(stringResId)
)
}
fun showSnackbarWithScope(stringResId: Int) {
viewModelScope.launch {
showSnackbar(stringResId)
}
_eventFlowSwitch.value = !_eventFlowSwitch.value
}
sealed class UIEvent {
data class ShowSnackbar(val messageResId: Int) : UIEvent()
}
companion object {
// time in ms, before we request new data
const val DELAY_BEFORE_REQUEST = 1500L
}
}
您可以使用 LocalContext
在任何可组合项中获取 Context
,然后在 LaunchedEffect
中使用它来获取您的资源:
val context = LocalContext.current
LaunchedEffect(true) {
context.resources.getString(event.messageResId)
}