使用 JetPack Compose 更改 Android 语言

Moh*_*chi 5 android kotlin android-jetpack android-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)

}
Run Code Online (Sandbox Code Playgroud)

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

但问题是不适用于 OutlinedTextField 或 Button,因为调用此函数时它们不会改变,甚至 rightToLeft 支持也不起作用,而且我找不到解决此问题的良好替代方案,因为我无法在项目中重新创建活动

And*_*Dev 8

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

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

Run Code Online (Sandbox Code Playgroud)

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

下面的代码是我从你的 Github 项目中获取的并添加了一个按钮。请注意如何使字符串资源 id 变得可变,并且此可变状态在文本中使用:

@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))
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,在任何地方使用尾随 lambda 表达式(包括单击事件处理程序)并且需要进行与语言相关的更改时,您都需要向这些 lambda 内的资源添加可变状态,如上所示。

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

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

https://github.com/JohannBlake/Jetmagic