updateConfiguration 后如何触发重组?
How to trigger recomposition after updateConfiguration?
我想动态更改我的应用程序语言,而不需要重新启动 Activity
以使结果生效。我现在正在做的是添加一个可变的 Boolean
状态,它是 switch 并且被所有 Text
元素使用。
为了更改语言,我在可点击回调中调用了以下代码(我将框用作虚拟对象,只是为了测试):
val configuration = LocalConfiguration.current
val resources = LocalContext.current.resources
Box(
modifier = Modifier
.fillMaxWidth()
.height(0.2.dw)
.background(Color.Red)
.clickable {
// to change the language
val locale = Locale("bg")
configuration.setLocale(locale)
resources.updateConfiguration(configuration, resources.displayMetrics)
viewModel.updateLanguage()
}
) {
}
然后它使用updateLanguage()
方法切换语言值
@HiltViewModel
class CityWeatherViewModel @Inject constructor(
private val getCityWeather: GetCityWeather
) : ViewModel() {
private val _languageSwitch = mutableStateOf(true)
var languageSwitch: State<Boolean> = _languageSwitch
fun updateLanguage() {
_languageSwitch.value = !_languageSwitch.value
}
}
问题是为了更新每个 Text
可组合项,我需要将 viewmodel
传递给所有使用 Text
的后代,然后使用一些错误的逻辑来强制更新每次,视图模型都会发生一些变化。
@Composable
fun SomeChildDeepInTheHierarchy(viewModel: CityWeatherViewModel, @StringRes textResId: Int) {
Text(
text = stringResource(id = if (viewModel.languageSwitch.value) textResId else textResId),
color = Color.White,
fontSize = 2.sp,
fontWeight = FontWeight.Light,
fontFamily = RobotoFont
)
}
它有效,但那是一些非常糟糕的逻辑,而且代码非常丑陋!是否有使用 Jetpack Compose 动态更改 Locale
的标准方法?
最简单的解决方案是在配置更改后重新创建 activity:
val context = LocalContext.current
Button({
// ...
resources.updateConfiguration(configuration, resources.displayMetrics)
context.findActivity()?.recreate()
}) {
Text(stringResource(R.string.some_string))
}
findActivity
:
fun Context.findActivity(): Activity? = when (this) {
is Activity -> this
is ContextWrapper -> baseContext.findActivity()
else -> null
}
如果出于某种原因你不想这样做,你可以用这样的新配置覆盖 LocalContext
:
MainActivity
:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val context = LocalContext.current
CompositionLocalProvider(
LocalMutableContext provides remember { mutableStateOf(context) },
) {
CompositionLocalProvider(
LocalContext provides LocalMutableContext.current.value,
) {
// your app
}
}
}
}
}
val LocalMutableContext = staticCompositionLocalOf<MutableState<Context>> {
error("LocalMutableContext not provided")
}
在您看来:
val configuration = LocalConfiguration.current
val context = LocalContext.current
val mutableContext = LocalMutableContext.current
Button(onClick = {
val locale = Locale(if (configuration.locale.toLanguageTag() == "bg") "en_US" else "bg")
configuration.setLocale(locale)
mutableContext.value = context.createConfigurationContext(configuration)
}) {
Text(stringResource(R.string.some_string))
}
请注意,remember
不会因系统配置更改而存在,例如屏幕旋转,您可能需要将选定的语言环境存储在某处,例如在 DataStore
中,并在提供 LocalMutableContext
.
时提供所需的配置,而不是我最初的 context
p.s。在这两种情况下,如果您根据 documentation 放置了资源,则视图模型中不需要标志,例如在 values-bg/strings.xml
中,依此类推,stringResource
开箱即用。
我想动态更改我的应用程序语言,而不需要重新启动 Activity
以使结果生效。我现在正在做的是添加一个可变的 Boolean
状态,它是 switch 并且被所有 Text
元素使用。
为了更改语言,我在可点击回调中调用了以下代码(我将框用作虚拟对象,只是为了测试):
val configuration = LocalConfiguration.current
val resources = LocalContext.current.resources
Box(
modifier = Modifier
.fillMaxWidth()
.height(0.2.dw)
.background(Color.Red)
.clickable {
// to change the language
val locale = Locale("bg")
configuration.setLocale(locale)
resources.updateConfiguration(configuration, resources.displayMetrics)
viewModel.updateLanguage()
}
) {
}
然后它使用updateLanguage()
方法切换语言值
@HiltViewModel
class CityWeatherViewModel @Inject constructor(
private val getCityWeather: GetCityWeather
) : ViewModel() {
private val _languageSwitch = mutableStateOf(true)
var languageSwitch: State<Boolean> = _languageSwitch
fun updateLanguage() {
_languageSwitch.value = !_languageSwitch.value
}
}
问题是为了更新每个 Text
可组合项,我需要将 viewmodel
传递给所有使用 Text
的后代,然后使用一些错误的逻辑来强制更新每次,视图模型都会发生一些变化。
@Composable
fun SomeChildDeepInTheHierarchy(viewModel: CityWeatherViewModel, @StringRes textResId: Int) {
Text(
text = stringResource(id = if (viewModel.languageSwitch.value) textResId else textResId),
color = Color.White,
fontSize = 2.sp,
fontWeight = FontWeight.Light,
fontFamily = RobotoFont
)
}
它有效,但那是一些非常糟糕的逻辑,而且代码非常丑陋!是否有使用 Jetpack Compose 动态更改 Locale
的标准方法?
最简单的解决方案是在配置更改后重新创建 activity:
val context = LocalContext.current
Button({
// ...
resources.updateConfiguration(configuration, resources.displayMetrics)
context.findActivity()?.recreate()
}) {
Text(stringResource(R.string.some_string))
}
findActivity
:
fun Context.findActivity(): Activity? = when (this) {
is Activity -> this
is ContextWrapper -> baseContext.findActivity()
else -> null
}
如果出于某种原因你不想这样做,你可以用这样的新配置覆盖 LocalContext
:
MainActivity
:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val context = LocalContext.current
CompositionLocalProvider(
LocalMutableContext provides remember { mutableStateOf(context) },
) {
CompositionLocalProvider(
LocalContext provides LocalMutableContext.current.value,
) {
// your app
}
}
}
}
}
val LocalMutableContext = staticCompositionLocalOf<MutableState<Context>> {
error("LocalMutableContext not provided")
}
在您看来:
val configuration = LocalConfiguration.current
val context = LocalContext.current
val mutableContext = LocalMutableContext.current
Button(onClick = {
val locale = Locale(if (configuration.locale.toLanguageTag() == "bg") "en_US" else "bg")
configuration.setLocale(locale)
mutableContext.value = context.createConfigurationContext(configuration)
}) {
Text(stringResource(R.string.some_string))
}
请注意,remember
不会因系统配置更改而存在,例如屏幕旋转,您可能需要将选定的语言环境存储在某处,例如在 DataStore
中,并在提供 LocalMutableContext
.
context
p.s。在这两种情况下,如果您根据 documentation 放置了资源,则视图模型中不需要标志,例如在 values-bg/strings.xml
中,依此类推,stringResource
开箱即用。