Android 使用 JetPack Compose 更改语言

Android Language change using JetPack Compose

我正尝试使用 Jetpack Compose 函数更改应用程序的语言环境,如下所示

  @Composable
 fun SetLanguage(position: Int) {
    val locale = Locale(
        when (position) {
            0 -> "ar"
            1 -> "en"
            2 -> "fr"
            else -> {
                "ar"
            }
        }
    )
    Locale.setDefault(locale)
    val configuration = LocalConfiguration.current
    configuration.setLocale(locale)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
        configuration.setLocale(locale)
    else
        configuration.locale = locale
    var resources = LocalContext.current.resources
    resources.updateConfiguration(configuration, resources.displayMetrics)

}

您可以在此处查看工作示例(没有按钮或文本字段) https://github.com/MakeItEasyDev/Jetpack-Compose-Multi-Language-Support

但是问题是 OutlinedTextField 或 Buttons 不工作,因为当调用此函数时它们不会改变,即使 rightToLeft 支持也不起作用,我找不到解决此问题的好方法,因为我无法重新创建 activity 在我的项目中

许多开发人员在开始使用 Compose 时遇到的问题是相信当发生重组时,可组合项中的所有内容都会被重组。这不是真的。 Compose 查看可组合签名并尝试确定自上次调用以来是否有任何变化。只有当参数values改变时才会调用该函数。在您发布在 Github 上的源代码中,它没有包含用于演示问题的按钮或大纲文本字段,因此我添加了一个。当您添加这样的按钮时:

Button(onClick = {}) {
     Text("Do Something")
}

Button 内的 Text 可组合项只会在初始组合发生时调用。但是当 Button 被重组时,Text 不会被重组,因为 Button 函数中的最后一个参数没有改变。 Lambda 函数不会改变。关于您的情况,更改语言确实会启动按钮的重组,但由于最后一个参数没有更改,因此永远不会调用 lambda 内部的内容(在本例中为 Text 可组合项)。要解决此问题,一种解决方案是使 Text 可组合项使用的字符串资源可变。任何可变的东西都会自动导致使用它的任何可组合项重新组合。

以下代码是我从您的 Github 项目中提取并添加了一个按钮。请注意字符串资源 ID 是如何变为可变的,并且该可变状态在 Text:

中使用
@Composable
fun LanguageContentPortrait(
    selectedPosition: Int,
    onLanguageSelected: (Int) -> Unit
) {
    val buttonTextResId by remember { mutableStateOf(R.string.hello) }

    CompositionLocalProvider(
        LocalLayoutDirection provides
                if (LocalConfiguration.current.layoutDirection == LayoutDirection.Rtl.ordinal)
                    LayoutDirection.Rtl
                else LayoutDirection.Ltr
    ) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {

            Spacer(modifier = Modifier.height(100.dp))
            ToggleGroup(selectedPosition = selectedPosition, onClick = onLanguageSelected)
            Spacer(modifier = Modifier.height(60.dp))
            Column(
                modifier = Modifier.fillMaxSize(),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                Text(
                    text = stringResource(id = R.string.content),
                    modifier = Modifier.fillMaxSize(),
                    textAlign = TextAlign.Center
                )

                Button(onClick = {}) {
                    Text(stringResource(buttonTextResId))
                }
            }
        }
    }
}

因此,在您使用尾随 lambda 表达式(包括点击事件处理程序)并且需要 language-dependent 发生更改的任何地方,您都需要向这些 lambda 内部的这些资源添加可变状态,如上所示。

尽管上述解决方案有效,但我不推荐使用它。大多数应用程序都会有很多依赖于语言的组件,并且必须为每个资源字符串创建一个可变状态会很痛苦。更好的解决方案是在语言发生变化时强制您的整个应用程序重新组合。由于Compose-only个app一般只有一个activity,会导致整个app重构。这将确保所有屏幕重组并强制所有文本可组合项重组,而无需为每个可组合项设置可变状态。您可以通过不同的方式强制您的应用重组整个 UI 树。不幸的是,Compose 不包含让您从头开始重新组合整个树的 API,因此唯一真正的解决方案是重新启动应用程序。

由于您的应用旨在处理设备配置更改(例如语言更改),您可能需要查看我开发的专门用于处理设备配置更改的 Compose 框架。它叫做 Jetmagic。它不仅处理语言更改,还处理所有其他更改,如屏幕方向、屏幕尺寸、屏幕密度和旧 view-based 系统使用的所有其他配置限定符。 Jetmagic 允许您像对待资源一样对待可组合项,而不仅仅是一堆函数,它以完全相同的方式处理它们 xml 资源在 view-based 系统下使用相同的算法进行处理。包含的示例应用程序还展示了更改设备的系统语言(在 Android 的设置下)或通过编程更改语言,如何使您的可组合 UIs 重新组合以正确的语言呈现内容:

https://github.com/JohannBlake/Jetmagic