问题描述
我有一个游戏,其中我有一个层次,一个季节具有多个级别,一个级别具有多个任务。
现在,我有一个活动,该活动为分层结构的各个部分(分为不同的字符串)显示祝贺消息。每个人都有:
- 惊叹
- 标题
- 说明
- 继续消息
例如季节:
- “万岁!”
- “赛季结束”
- “您已经成功完成了第X季”
- “继续第X + 1季”
enum class CongratulationsType(val exclamation: String,val header: String) {
SEASON("HURRAY!","You finished season %d!!"),LEVEL("YAY!","Well done! Level over!"),TASK("NICE!","This looked too easy")
}
...
val headerString = CongratulationsType.SEASON.header
但是字符串应该保存在资源中。因此,我必须将字符串放入资源中,并将资源正确链接到枚举值。
这意味着我需要做两次工作。我将在资源文件中定义字符串时不使用“绑定结构”,而只是将它们放入枚举中以具有“绑定结构”。 “绑定结构”是指上述结构,其中存在特定字段的强制性结构,并且可以调用类似CongratulationsType.SEASON.header
之类的结构。
我的问题是:是否可以将字符串“打包”成某种定义的结构并使该结构像枚举或哈希图一样?像这样:
包装定义
<resources>
<pack_def name="congrats">
<string name="exclamation"/>
<string name="header"/>
...
</pack_def>
<resources>
打包实施
<resources>
<pack name="season" type="congrats">
<string name="exclamation">HURRAY!</string>
<string name="header">Season Finished</string>
...
</pack>
<resources>
// In code
//// function deFinition
fun setMessage(congrats: Congrats) {
exclamationTextView.text = congratsType.exclamation
headerTextView.text = congratsType.header
...
}
//// function use
setMessage(Congrats.Season)
// In Resources
<TextView
android:text="@packs/congrats/season/exclamation"
/>
我知道我可以使用字符串数组资源,但是它们看起来有点“不干净”,因为正如我所说,我希望结构被“绑定”并像枚举一样被调用。我特别要求一种方法,使其尽可能接近枚举,而不必为每个枚举值的每个字段分配重新分配。
解决方法
我的问题是:是否可以将字符串“打包”成某种定义的结构并使该结构像枚举或哈希图一样?像这样:
android:text="@packs/congrats/season/exclamation"
简短回答-不。 Android将不知道如何从这些新结构中解析和提取值。
您可以做什么:
- 创建4个字符串资源数组:感叹号,标题,描述,继续消息;
- 使用4个字符串变量创建kotlin数据类。让我们称之为
Congratulation
; - 将资源映射到具有
Congratulation
s的数组。
示例:
在strings.xml文件中,声明下一个数组:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="exclamations">
<item>Exclamation 1!!!</item>
<item>Exclamation 2!!!</item>
...
<item>Exclamation N!!!</item>
</string-array>
<string-array name="headers">
<item>Header 1</item>
<item>Header 2</item>
...
<item>Header N</item>
</string-array>
<!-- etc. -->
</resources>
或者您可以在这些数组中使用已定义的资源,以便对其进行排序:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="exclamations">
<item>@string/exclamation_1</item>
<item>@string/exclamation_2</item>
...
<item>@string/exclamation_n</item>
</string-array>
<!-- etc. -->
</resources>
定义数据类:
data class Congratulation(val exclamation: String,val header: String,val description: String,val continueMessage: String)
地图资源:
val exclamations = context.resources.getStringArray(R.array.exclamations)
val headers = context.resources.getStringArray(R.array.headers)
val descriptions = context.resources.getStringArray(R.array.descriptions)
val continueMessages = context.resources.getStringArray(R.array.continue_messages)
val congratulations = exclamations.mapIndexed { i,exclamation ->
Congratulation(exclamation,headers[i],descriptions[i],continueMessages[i])
}
专业人士
- 无需在代码中提及每个字符串资源,只需提及4个数组资源;
- 在第一点之后,每次添加新值时,都不需要修改代码。您可以在应用程序启动时初始化此数组,然后再使用。
缺点
- 翻译顺序必须准确!如果您不小心在
<string-array>
内更改了订单,则不会出现警告。您将必须手动检查订单或创建特定的测试; - 如果不小心将更少的条目添加到
headers
,descriptions
或continueMessages
数组中的任何一个,则应用程序将因IndexOutOfBoundsException
而崩溃。虽然,当您尝试映射值时它将崩溃,并且易于调试。 - 无法使用
when
语句。
这并不是您所要的,但是万一消息中唯一改变的是季节号,您可以使用format strings并在显示消息时传递适当的数字:
<string name="continue_message">Continue to season %d</string>
continueTextView.text = getString(R.string.continue_message,season+1)
如果您确实希望每个季节都可以设置任意字符串,那么Jenea认为我是对的-无法以这种方式在字符串XML中强制执行结构(我对XML并不十分熟悉一般而言,您可以使用它来做一些花哨的事情,但我认为它们在Android实际解释和使用的内容方面受到限制。您可以进行运行时验证(可能仅在调试版本中,对所有语言环境进行彻底检查),但必须进行设计,以便捕获可能发生的任何问题
编辑只是为了阐明我对模板的意思
如果您实际上有4个字符串,只是根据季节编号,级别编号等因素有很多变化,您也许可以使用格式字符串,因此您只需要 在您的字符串文件中输入4:
<string name="season_exclamation">HURRAY!</string>
<string name="level_exclamation">YAY!</string>
<string name="season_header">You finished season %d!!</string>
<string name="level_header">Well done! Level over!</string>
这样,您可以强制执行结构,没有可变长度的数组或任何东西-您只有4个字符串资源和4个特定的ID(如果最终将它们本地化,则会在未提供的情况下向您发出警告全部)。
因此,您可以轻松地创建一个枚举,只是您的字符串是在其他位置定义的。
enum class Congratulations(
@StringRes private val headerResId: Int,@StringRes private val exclamationResId: Int
) {
SEASON(R.string.season_header,R.string.season_exclamation),LEVEL(R.string.level_header,R.string.level_exclamation);
fun getHeader(context: Context) = context.getString(headerResId)
// etc
}
唯一棘手的事情是您需要在该上下文中传递信息以提取适当的字符串资源,而不是仅声明字符串文字。如果需要,您可以在课程上设置一个Context
(在访问任何字符串之前),并执行类似的操作
enum class Congratulations(
...
...
companion object {
lateinit var context: Context
}
val header: String get() = context.getString(R.string.header)
// or,so you only grab the strings once
val exclamation: String by lazy { context.getString(R.string.exclamation) }
}
关于格式字符串的要点是,您无需为“ bla bla season 1”和“ bla bla season 2”创建单独的字符串资源,并且可以不断添加到strings
文件中使用模板字符串,您可以在枚举上使用fun getContinueMessage(seasonNumber): String
之类的函数,然后就不需要SEASON1,SEASON2,SEASON3枚举。尽管在那时,最好还是使用密封类,因为并不是所有的Congratulation
类型都需要密封类,并且密封类将允许您包含不同的对象,而不是包含枚举的对象,而枚举类型是完全相同的类型>