请在此处找到我的 Kotlin - Jetpack Compose 项目的简化用例:
我需要构建一个 50 x 50 字符串的列表(我们称之为 listC)。该列表由 2 个包含 50 个字符串的列表组成:listA 和 listB。每个字符串都是本地化的,因此必须从字符串资源中检索它。
这就是为什么我创建了一个 MyData 类,如下(简化的代码语法):
class MyData(val context: Context) {
var listA : MutableList<String> = mutableListOf()
var listB : MutableList<String> = mutableListOf()
var listC : MutableList<String> = mutableListOf()
init{
initializeData()
}
fun initializeData() {
listA.apply {
add(context.resources.getString(R.string.valueA1)
...
add(context.resources.getString(R.string.valueA50)
}
listB.apply {
add(context.resources.getString(R.string.valueB1)
...
add(context.resources.getString(R.string.valueB50)
}
for a in listA
for b in listB
listC.add("$a: $b")
}
listC 项目具有简化语法“$a: $b”,但显示的字符串可以是“$a: $b”、“$b: $a”、“$a”或“$ b”。
这些 50 x 50 字符串之一将根据 ScreenViewModel 中定义的业务逻辑向用户显示,因此应在 ScreenViewModel 中检索数据,如下所示:
class ScreenViewModel : ViewModel() {
val context = LocalContext.current
var myData = MyData(context)
}
显然这是非常糟糕的做法,但我完全不知道为什么也不知道如何做得更好。
您能帮我学习实现这一目标的正确方法吗?
对于初学者来说,这是一个很难掌握的话题:我应该学习哪些资源才能更好地理解这个问题?
非常感谢。
此时,您可能已经知道您想要执行的操作的最佳实践,但我对您的代码进行了一些操作,并尝试应用“在 ViewModel 中使用字符串资源”一文中的建议。任何人都可以随意提出如何改进这一点或提出问题。 在
strings.xml
中,我有(2 个列表中的每一个都缩短为 4 项):
<resources>
<string name="valueA1">turtle pie</string>
<string name="valueA2">vanilla gelato</string>
<string name="valueA3">strawberry shortcake</string>
<string name="valueA4">pan au chocolat</string>
<string name="valueB1">espresso</string>
<string name="valueB2">black tea</string>
<string name="valueB3">chamomile tea</string>
<string name="valueB4">pour-over coffee</string>
<!-- other string resources here -->
</resources>
对于 ViewModel,我在这里没有使用
val context = LocalContext.current
,因为我相信它只能在可组合函数中工作。当此 ViewModel 在 Composable 中实例化时,它的
init
函数就会运行。我将字符串资源 ID 添加到列表 A 和 B(Int 数据类型),然后创建一个 Pairs 列表,即我的列表 C。最后,从列表 C 中选取一个项目,然后作为 LiveData pickedItem
值发出。我的ScreenViewModel.kt
看起来像这样:class ScreenViewModel : ViewModel() {
// Lists A and B contain Int string resource IDs only, not actual string values
private val _strResIdsA = mutableListOf<Int>()
private val _strResIdsB = mutableListOf<Int>()
// List C is a list that will contain paired Int resource IDs from lists A and B
private var _strResIdPairsC = mutableListOf<Pair<Int, Int>>()
// Use LiveData to allow the UI to observe and use the value of the picked item once it's set
private val _pickedItem by lazy { MutableLiveData<StringValue>() }
val pickedItem: LiveData<StringValue> get() = _pickedItem
init {
// Populate lists A, B, and C and then
// start the pick an item after invoking the callback (to ensure list population is done)
initializeData {
// Pick a list C item based on below function's logic
val pickedItemStrResPair = pickFromListCPairs()
// Emit value of picked item
val stringValue =
StringValue.StringResourcePair(
pickedItemStrResPair.first,
pickedItemStrResPair.second
)
_pickedItem.postValue(stringValue)
}
}
private fun createListCPairs(): List<Pair<Int, Int>> {
// Create an empty mutable list of pairs
val pairs = mutableListOf<Pair<Int, Int>>()
// Populate the list of pairs based on list A and list B values
for (resIdA in _strResIdsA) {
for (resIdB in _strResIdsB) {
val pair = Pair(resIdA, resIdB)
pairs.add(pair)
}
}
return pairs.toList()
}
private fun pickFromListCPairs(): Pair<Int, Int> {
//Change logic as needed; Currently picks randomly
val randomItemIndex = Random.nextInt(_strResIdPairsC.size);
return _strResIdPairsC[randomItemIndex]
}
private fun initializeData(callback: () -> Unit) {
// Populate list A
_strResIdsA.apply {
add(R.string.valueA1)
add(R.string.valueA2)
add(R.string.valueA3)
add(R.string.valueA4)
//todo Add the rest of the string resources
}
// Populate list B
_strResIdsB.apply {
add(R.string.valueB1)
add(R.string.valueB2)
add(R.string.valueB3)
add(R.string.valueB4)
//todo Add the rest of the string resources
}
// Populate list C
_strResIdPairsC = createListCPairs().toMutableList()
Log.i("logtag","list of pairs (size of ${_strResIdPairsC.size}): $_strResIdPairsC")
// Invoke callback once population of lists is done
callback.invoke()
}
}
我的
MainActivity.kt
有一个可组合函数,可以实例化 ViewModel 并观察其
pickedItem
LiveData。这就是我把你的val context = LocalContext.current
移到的地方。一旦 pickedItem
数据更新,我们就会在 Text
元素中显示其值。请注意,文本的值使用关键字 var pickedItemText by remember { mutableStateOf("") }
声明为 remember
,以确保 Text
元素的值将在“重组”时更新。class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AnythingGoesTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
DisplayPickedItem()
}
}
}
}
}
@Composable
fun DisplayPickedItem() {
// Instantiate ViewModel
val vm = viewModel<ScreenViewModel>()
// Get context here instead of within the ViewModel
val context = LocalContext.current
// Declare variable for picked item's value here
var pickedItemText by remember { mutableStateOf("") }
// Observe to wait for emitted value from the pickedItem LiveData of the ViewModel
vm.pickedItem.observe(LocalLifecycleOwner.current) {
// Set observed string value to display using Text later
pickedItemText = it.asString(context)
Log.d("logtag", "pickedItemText=$pickedItemText")
}
// Display text output
Text(text = pickedItemText)
}
我没有使用您的
MyData
类,而是基于
在 ViewModel 中使用字符串资源文章创建了这个
StringValue
类,其中我添加了一个新的 class StringResourcePair
来支持我对字符串资源 ID 对的实现:sealed class StringValue {
data class DynamicString(val value: String) : StringValue()
object Empty : StringValue()
class StringResource(
@StringRes val resId: Int,
vararg val args: Any
) : StringValue()
// I added this to let us use the pair of string resources from list C
class StringResourcePair(
@StringRes val resIdA: Int,
@StringRes val resIdB: Int,
vararg val args: Any
) : StringValue()
fun asString(context: Context?): String {
return when (this) {
is Empty -> ""
is DynamicString -> value
is StringResource -> context?.getString(resId, *args).orEmpty()
is StringResourcePair -> {
val stringA = context?.getString(resIdA, *args).orEmpty()
val stringB = context?.getString(resIdB, *args).orEmpty()
"$stringA: $stringB"
}
}
}
}
一些阅读资源:
LiveData: