首页 > 解决方案 > 在android资源中定义具有不同内容但结构相同的字符串包

问题描述

我有一个游戏,我有一个层次结构,一个赛季有多个级别,一个级别有多个任务。

现在,我有一个活动,它为层次结构的每个部分显示一条祝贺消息,这些部分分为不同的字符串。他们每个人都有:

例如季节:

我的第一个想法是创建一个包含适当值的枚举。像这样:

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"
/>

我知道我可以使用字符串数组资源,但它们看起来有点“不干净”,因为正如我所说,我希望结构是“绑定的”并像枚举一样调用。我特别要求一种方法尽可能接近枚举,而不必为每个枚举值的每个字段分配资源。

标签: androidkotlinenumsandroid-resources

解决方案


我的问题是:是否可以将字符串“打包”在某个已定义的结构中,并使该结构类似于枚举或哈希图?就像是:

android:text="@packs/congrats/season/exclamation"

简短的回答 - 不。Android 将不知道如何从这些新结构中解析和提取值。

你可以做什么:

  1. 创建4个字符串资源数组:Exclamations、Headers、Descriptions、Continue Messages;
  2. 使用 4 个字符串变量创建 kotlin 数据类。让我们称之为Congratulation;
  3. 将资源映射到具有Congratulations 的数组。

例子:

在 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,descriptionscontinueMessages数组 - 应用程序将与IndexOutOfBoundsException. 虽然,一旦您尝试映射值,它就会崩溃并且很容易调试。
  • 使用when语句变得不可能。

推荐阅读