我是一名初级程序员,正在创建一个足球应用程序来模拟锦标赛的小组赛阶段。然而,我遇到了一个我无法处理的问题。我创建了一个屏幕,顶部有用于更改组的按钮,但是当我更改组时,ScoreInput 字段(它是 OutlinedTextField)具有上一个组中的值,我希望这些值成为初始值,即 0:0。我意识到这仍然是同一个 TextField,我只是更改了旁边显示的团队,但我不知道如何解决这个问题(创建 6 个看起来相同的单独屏幕似乎也不是一个好的选择)给我的想法)。我还意识到应用程序不应该在 UI 层包含太多逻辑,尽管我想在稍后阶段进行优化。 我希望有人可以帮助我,并提前非常感谢您的帮助。
此外,需要保留用户无法在同一组中编辑已批准结果的选项,只有在更改组后才能进行后续申请阶段
我的屏幕代码:
@Composable
fun GroupStageScreen(viewModel: EuroViewModel) {
val groups = listOf("A", "B", "C", "D", "E", "F")
val (matchResult, setMatchResult) = remember { mutableStateOf(MutableList(6) { EuroMatchResult(0, 0) }) }
val groupData by viewModel.groupData.collectAsState(emptyList())
val (selectedGroup, setSelectedGroup) = remember { mutableStateOf(groups[0]) }
Surface(
modifier = Modifier.fillMaxSize(),
color = background_color
) {
Column(
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally
) {
// Buttons for changing groups
Row(
modifier = Modifier
.padding(5.dp)
.padding(top = 10.dp),
verticalAlignment = Alignment.Top,
horizontalArrangement = Arrangement.Center
) {
groups.forEach { groupInfo ->
Button(
onClick = {
setSelectedGroup(groupInfo)
},
modifier = Modifier.padding(2.dp),
shape = RoundedCornerShape(15.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Gray,
contentColor = Color.White
)
) {
Text(text = groupInfo)
}
}
}
val selectedGroupData = groupData.find { it.group == selectedGroup }
selectedGroupData?.let {groupInfo ->
// Group Table
Text(
text = "Group ${groupInfo.group}",
color = Color.White,
style = MaterialTheme.typography.h4,
textAlign = TextAlign.Center
)
Surface(
modifier = Modifier
.padding(7.dp)
.padding(top = 10.dp),
color = Color.Gray,
shape = RoundedCornerShape(10.dp),
border = BorderStroke(2.dp, Color.Gray),
elevation = 10.dp
) {
TeamTable(groupInfo.teams)
}
// Fields to fill the results
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Enter the match results: ",
color = Color.White,
style = MaterialTheme.typography.h6
)
Spacer(modifier = Modifier.height(8.dp))
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(bottom = 30.dp)
) {
for (i in 0 until groupInfo.teams.size - 1) {
for (j in i + 1 until groupInfo.teams.size) {
val teamA = groupInfo.teams[i]
val teamB = groupInfo.teams[j]
val result = matchResult[i * 2 + j - (i * 1)]
MatchResultInput(
teamA = teamA.shortName,
teamB = teamB.shortName,
matchResult = result,
onResultChanged = { newResult ->
val updatedMatchResult = matchResult.toMutableList()
updatedMatchResult[i * 2 + j - (i + 1)] = newResult
setMatchResult(updatedMatchResult)
if (newResult.scoreA > newResult.scoreB) {
teamA.matchesPlayed++
teamA.matchesWon++
teamA.points += 3
teamB.matchesPlayed++
teamB.matchesLost++
} else if (newResult.scoreA < newResult.scoreB) {
teamA.matchesPlayed++
teamA.matchesLost++
teamB.matchesPlayed++
teamB.matchesWon++
teamB.points += 3
} else {
// It's a draw
teamA.matchesPlayed++
teamA.matchesDrawn++
teamA.points++
teamB.matchesPlayed++
teamB.matchesDrawn++
teamB.points++
}
})
}
}
}
}
}
}
}
@Composable
fun MatchResultInput(
teamA: String,
teamB: String,
matchResult: EuroMatchResult,
onResultChanged: (EuroMatchResult) -> Unit) {
val scoreA = remember { mutableStateOf(matchResult.scoreA.toString()) }
val scoreB = remember { mutableStateOf(matchResult.scoreB.toString()) }
val showPredictions = remember { mutableStateOf(false) }
val buttonEnabled = remember { mutableStateOf(true) }
Surface(
modifier = Modifier
.padding(start = 7.dp, end = 7.dp, top = 20.dp),
color = Color.Gray,
shape = RoundedCornerShape(topStart = 40.dp, topEnd = 40.dp), //30
border = BorderStroke(3.dp, Color.White),
elevation = 10.dp
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(4.dp)
.padding(start = 15.dp, end = 15.dp, bottom = 5.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(end = 8.dp)
) {
Image(
painterResource(id = R.drawable.flag_poland),
contentDescription = null,
modifier = Modifier.size(70.dp) //70
)
Text(
text = teamA,
fontSize = 19.sp
)
}
ScoreInput(
scoreState = scoreA,
onAction = KeyboardActions { FocusRequester.Default.requestFocus() },
enabled = true,
onScoreChanged = {newScore ->
scoreA.value = newScore
}
)
Column(
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = ":",
modifier = Modifier.padding(top = 40.dp,bottom = 12.dp),
textAlign = TextAlign.Center)
Button(
onClick = {
onResultChanged(EuroMatchResult(scoreA.value.toIntOrNull() ?: 0, scoreB.value.toIntOrNull() ?: 0))
showPredictions.value = true
buttonEnabled.value = false
},
modifier = Modifier.size(34.dp),
shape = CircleShape,
colors = ButtonDefaults.buttonColors(backgroundColor = green_check, contentColor = Color.White, disabledBackgroundColor = little_green_check),
enabled = buttonEnabled.value) {
Text(text = "✓", fontSize = 12.sp, textAlign = TextAlign.Center, fontWeight = FontWeight.Bold)
}
}
ScoreInput(scoreState = scoreB,
onScoreChanged = { newScore ->
scoreB.value = newScore
})
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(start = 8.dp)
) {
Image(
painterResource(id = R.drawable.flag_germany),
contentDescription = null,
modifier = Modifier.size(70.dp)
)
Text(
text = teamB,
fontSize = 19.sp
)
}
}
}
Surface(modifier = Modifier.padding(start = 7.dp, end = 7.dp),
color = Color.White,
shape = RoundedCornerShape(bottomStart = 40.dp, bottomEnd = 40.dp), //30
elevation = 10.dp) {
Row(modifier = Modifier
.fillMaxWidth()
.padding(top = 5.dp, bottom = 5.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center) {
if (!showPredictions.value){
Text(
text = "Confirm the result to see other players predictions",
modifier = Modifier.padding(7.dp),
textAlign = TextAlign.Center)
} else {
Text(text = "33%", modifier = Modifier.weight(1f), textAlign = TextAlign.Center, fontSize = 20.sp, fontWeight = FontWeight.Bold)
Text(text = "34%", modifier = Modifier.weight(1f), textAlign = TextAlign.Center, fontSize = 20.sp, fontWeight = FontWeight.Bold)
Text(text = "33%", modifier = Modifier.weight(1f), textAlign = TextAlign.Center, fontSize = 20.sp, fontWeight = FontWeight.Bold)
}
}
}
}
@Composable
fun TeamTable(teams: List<Team>) {
val sortedTeams = teams.sortedByDescending { it.points }
Column(modifier = Modifier.padding(3.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
val headerTitles = listOf("M", "W", "D", "L", "Pts")
Text(
"Country",
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier.width(150.dp)
)
headerTitles.forEach { titles ->
Text(
text = titles,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier.width(50.dp)
)
}
}
sortedTeams.forEachIndexed { index, team ->
Surface(color = Color.White) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Image(
painterResource(id = R.drawable.flag_poland),
contentDescription = null,
modifier = Modifier.size(30.dp)
)
Text(
team.name,
textAlign = TextAlign.Start,
modifier = Modifier
.width(120.dp)
.padding(start = 10.dp)
)
listOf(
team.matchesPlayed,
team.matchesWon,
team.matchesDrawn,
team.matchesLost,
team.points).forEach { value ->
Text(
text = value.toString(),
textAlign = TextAlign.Center,
modifier = Modifier.width(50.dp)
)
}
}
Divider(thickness = 2.dp)
}
}
}
}
data class EuroMatchResult(
val scoreA: Int, val scoreB: Int
)
data class GroupData(
val group: String,
val teams: List<Team>
)
data class Team(
val name: String,
val shortName: String,
var matchesPlayed: Int = 0,
var matchesWon: Int = 0,
var matchesDrawn: Int = 0,
var matchesLost: Int = 0,
var points: Int = 0,
)
(我希望我答对了你的问题。)
我猜您期望的行为是,当
matchResult
的值在 MatchResultInput
中更改时,您的文本字段将使用新值进行更新。然而,事实并非如此。 State
对象在重组过程中保持不变,无论触发它的是什么,包括可组合项参数的更改。
有 2 种方法可以更改文本输入
“正确的”是不使用这两个状态对象。相反,只需将状态传递给
MatchResultInput
两个函数 updateScoreA
和 updateScoreB
以及 hoist 到父组件即可。现在您可以直接使用 matchResult
的值作为文本输入的参数,并且每当父组件更新它时,您的文本输入也应该更新
更快的解决方案,只需添加一个依赖于
matchResult.scoreA
的 LaunchedEffect 即可在分数发生变化时更新分数 A 的状态,并为分数 B 更新另一个状态