【发布时间】:2019-03-12 20:38:02
【问题描述】:
当我不使用ViewModel 中的Context 相关类(活动、片段、服务、视图等)或保留所有业务逻辑的自定义构建类时,我需要访问应用程序的本地化字符串。例如,在LiveData 的帮助下将String 发布到UI:
toastMessageLiveData.value = buildLastLoginToastMessage()
fun buildLastLoginToastMessage() {
val lastLoginDiff = (System.currentMillis() - getLastLogin()) / DateUtils.HOURS_IN_MILLIS
return MyApp.instance.getString(R.string.last_login_txt, lastLoginDiff)
}
现在,在 Activity 中,我正在观察 LiveData 的变化,并显示 Toast 消息。
这很好用,但是如果用户更改应用程序的语言就会出现问题。布局已更新,我可以看到使用了新语言环境的语言,但 MyApp.instance.getString(R.string....) 函数仍在使用旧语言环境。如果我强制终止应用程序并重新启动它,它会工作,因为再次调用应用程序的 attachBaseContext 并应用新的区域设置。但是,我不想强行终止应用程序,我需要一个解决方案来更新应用程序的配置。
创建或更新Context的实用函数
fun buildLocalizedContext(context: Context): Context {
// From preferences, load the saved Language and Country codes
val language = getSavedLanguage()
val country = getSavedCountry()
// Create the new locale
val locale = Locale(language, country)
Locale.setDefault(locale)
val resources = context.resources
val configuration = Configuration(resources.configuration)
// Create a new configration or update the existing one if the API is less than 17
if (Build.VERSION.SDK_INT >= 17) {
configuration.setLocale(locale)
return context.createConfigurationContext(configuration)
} else {
configuration.locale = locale
resources.updateConfiguration(configuration, resources.getDisplayMetrics())
}
return context
}
当应用启动时,应用新的语言环境
class MyApp: Application {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(LocaleHelper.instance.buildLocalizedContext(base))
}
}
其他活动正在使用的BaseActivity 类:
class BaseActivity: AppCompatActivity {
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LocaleHelper.instance.buildLocalizedContext(newBase))
}
}
这是我到目前为止所做的,但这也不起作用:
fun changeLocale(locale: Locale, app: Application) {
Locale.setDefault(locale)
val resources = app.resources
val configuration = resources.configuration
if (Build.VERSION.SDK_INT >= 17) {
configuration.setLocale(locale)
}
else {
configuration.locale = locale
resources.updateConfiguration(configuration, resources.displayMetrics)
}
}
上面的代码 sn -p 不起作用,它不会改变应用程序的区域设置,只有当我强制重启应用程序时。
【问题讨论】:
-
“这很好用,但如果用户更改应用程序的语言,问题就来了”——应用程序并没有真正的语言。您的
setLocale()方法一直有点像 hack,在 Android 中的支持参差不齐。话虽如此,是否有一些解决方案涉及您不直接访问Application,而是通过一些ContextWrapper,您可以在其中更改ContextWrapper使用的语言环境? -
@Commons - 一种解决方案是将字符串资源 id 发送到
Live Data并从Activity或Fragment获取字符串,但这通过移动业务打破了 MVVM 原则UI 的逻辑。在上面的例子中,你可以看到计算上次登录和构建字符串的业务逻辑是在ViewModel中进行的。在ViewModel中调用MyApp.instance.getString(R.string....)函数时会出现问题,因为只有在我重新启动应用程序时,MyApp.instance.configuration.locale中的Locale才会更新为新的语言环境。 -
我的意思是,除了调用
MyApp.instance.getString(),也许您可以使用除MyApp.instance之外的其他东西(例如MyApp.instance周围的ContextWrapper),它在范围内仍然是全局的,但可以在运行时替换。或者,也许字符串是通过调用函数创建的,调用者提供Activity。 -
@CommonsWare - 感谢您的快速回复。如果你能给我一个例子,我如何在
MyApp.instance周围使用或包裹ContextWrapper,或者如何在不违反MVVM原则的情况下从ViewModel访问Activity。 -
关于后者,
fun buildLastLoginToastMessage(ctxt: Context)在您的上述代码 sn-p 中,该函数由您的活动或片段调用并传入活动,您使用ctxt作为您的 @ 987654361@电话。就前者而言,我只是建议您自己研究的途径。就个人而言,我永远不会编写使用setLocale()的应用程序。