feat: 出牌记录弹窗重构,上下箭头外置、宽度拉满、纯文字区域扩高;状态条扩展至2行
This commit is contained in:
parent
13a1e9432a
commit
cdfcce357d
@ -65,8 +65,8 @@ fun GameScreen(
|
|||||||
.verticalScroll(scrollState)
|
.verticalScroll(scrollState)
|
||||||
.padding(top = 24.dp, bottom = 100.dp)
|
.padding(top = 24.dp, bottom = 100.dp)
|
||||||
) {
|
) {
|
||||||
// Game message - fixed height area
|
// 出牌状态条,最多2行
|
||||||
Box(modifier = Modifier.fillMaxWidth().heightIn(min = 36.dp)) {
|
Box(modifier = Modifier.fillMaxWidth().heightIn(min = 36.dp, max = 56.dp)) {
|
||||||
if (gameState.message.isNotEmpty()) {
|
if (gameState.message.isNotEmpty()) {
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -83,8 +83,10 @@ fun GameScreen(
|
|||||||
Text(
|
Text(
|
||||||
text = gameState.message,
|
text = gameState.message,
|
||||||
color = GoldAccent,
|
color = GoldAccent,
|
||||||
fontSize = 14.sp,
|
fontSize = 13.sp,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
Icon(
|
Icon(
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
package com.unogame.ui.screens
|
package com.unogame.ui.screens
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
import androidx.compose.animation.core.spring
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@ -12,6 +17,8 @@ import androidx.compose.material3.*
|
|||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.draw.scale
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.SpanStyle
|
import androidx.compose.ui.text.SpanStyle
|
||||||
@ -371,91 +378,129 @@ fun LocalGameScreen(
|
|||||||
val creamBg = Color(0xFFFFF8E1) // 护眼米白
|
val creamBg = Color(0xFFFFF8E1) // 护眼米白
|
||||||
val dimText = Color(0xFF555555) // 浅色底上的暗灰文字
|
val dimText = Color(0xFF555555) // 浅色底上的暗灰文字
|
||||||
|
|
||||||
// 关闭
|
// 列表状态放在外层供标题栏和内容区共享
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
val canScrollForward by remember { derivedStateOf { listState.canScrollForward } }
|
||||||
|
val canScrollBackward by remember { derivedStateOf { listState.canScrollBackward } }
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
|
modifier = Modifier.fillMaxWidth(0.98f),
|
||||||
onDismissRequest = { showLogDialog = false },
|
onDismissRequest = { showLogDialog = false },
|
||||||
title = { Text("出牌记录", color = Color(0xFF333333), fontWeight = FontWeight.Bold) },
|
title = {
|
||||||
text = {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Column(
|
Text("出牌记录", color = Color(0xFF333333), fontWeight = FontWeight.Bold, modifier = Modifier.weight(1f))
|
||||||
modifier = Modifier
|
if (gameLog.size > 4 && canScrollBackward) {
|
||||||
.heightIn(min = 300.dp, max = 420.dp)
|
var topPressed by remember { mutableStateOf(false) }
|
||||||
.verticalScroll(rememberScrollState())
|
val topScale by animateFloatAsState(if (topPressed) 1.4f else 1f, spring())
|
||||||
) {
|
if (topPressed) {
|
||||||
if (gameLog.isEmpty()) {
|
LaunchedEffect(Unit) { kotlinx.coroutines.delay(400); topPressed = false }
|
||||||
Text("暂无记录", color = dimText, fontSize = 13.sp)
|
|
||||||
}
|
|
||||||
gameLog.forEachIndexed { index, msg ->
|
|
||||||
val playerName = gameLogPlayers.getOrElse(index) { parsePlayerName(msg) }
|
|
||||||
val avatar = getBotAvatar(playerName)
|
|
||||||
val colorText = parseColorText(msg)
|
|
||||||
val cardColor = cardColorMap[colorText]
|
|
||||||
val isPlay = " 出了 " in msg
|
|
||||||
val arrowColor = parseArrowColor(msg)
|
|
||||||
val afterPlayer = msg.substringAfter(playerName)
|
|
||||||
|
|
||||||
// 用 AnnotatedString 构建整行,支持自动换行
|
|
||||||
val annotated = buildAnnotatedString {
|
|
||||||
// 序号
|
|
||||||
withStyle(SpanStyle(color = Color(0xFF999999), fontSize = 13.sp)) {
|
|
||||||
append("${gameLog.size - index}. ")
|
|
||||||
}
|
|
||||||
// 玩家名
|
|
||||||
withStyle(SpanStyle(color = avatar.color, fontWeight = FontWeight.Bold, fontSize = 13.sp)) {
|
|
||||||
append(playerName)
|
|
||||||
}
|
|
||||||
if (isPlay && (colorText.isNotEmpty() || arrowColor != null)) {
|
|
||||||
if (arrowColor != null) {
|
|
||||||
val prefix = afterPlayer.substringBefore(" 万能")
|
|
||||||
val cardBlock = afterPlayer.substringAfter(" 出了 ").substringBefore(",")
|
|
||||||
val remain = afterPlayer.substringAfter(prefix + " 出了 " + cardBlock)
|
|
||||||
withStyle(SpanStyle(color = dimText, fontSize = 13.sp)) {
|
|
||||||
append(prefix)
|
|
||||||
append(" 出了 ")
|
|
||||||
}
|
|
||||||
withStyle(SpanStyle(color = Color.Black, fontWeight = FontWeight.Black,
|
|
||||||
fontSize = 13.sp, background = arrowColor.second.copy(alpha = 0.9f))) {
|
|
||||||
append(cardBlock)
|
|
||||||
}
|
|
||||||
if (remain.isNotEmpty()) {
|
|
||||||
withStyle(SpanStyle(color = Color(0xFF777777), fontSize = 12.sp)) {
|
|
||||||
append(remain)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val prefix = afterPlayer.substringBefore(colorText)
|
|
||||||
val cardId = colorText + afterPlayer.substringAfter(colorText).substringBefore(",")
|
|
||||||
val remain = afterPlayer.substringAfter(prefix + cardId)
|
|
||||||
withStyle(SpanStyle(color = dimText, fontSize = 13.sp)) {
|
|
||||||
append(prefix)
|
|
||||||
}
|
|
||||||
withStyle(SpanStyle(color = Color.Black, fontWeight = FontWeight.Black,
|
|
||||||
fontSize = 13.sp, background = (cardColor ?: Color.Gray).copy(alpha = 0.9f))) {
|
|
||||||
append(cardId)
|
|
||||||
}
|
|
||||||
if (remain.isNotEmpty()) {
|
|
||||||
withStyle(SpanStyle(color = Color(0xFF777777), fontSize = 12.sp)) {
|
|
||||||
append(remain)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
withStyle(SpanStyle(color = dimText, fontSize = 13.sp)) {
|
|
||||||
append(afterPlayer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Text(annotated, modifier = Modifier.padding(vertical = 5.dp))
|
|
||||||
// 条目间加细分割线
|
|
||||||
if (index < gameLog.size - 1) {
|
|
||||||
Spacer(modifier = Modifier.height(1.dp))
|
|
||||||
}
|
}
|
||||||
|
IconButton(
|
||||||
|
onClick = { topPressed = true; scope.launch { listState.animateScrollToItem(0) } },
|
||||||
|
modifier = Modifier.size(28.dp).scale(topScale)
|
||||||
|
) { Icon(Icons.Default.KeyboardArrowUp, "到顶部", tint = Color(0xFF555555)) }
|
||||||
|
} else {
|
||||||
|
Spacer(modifier = Modifier.size(28.dp)) // 占位保持标题居中
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
text = {
|
||||||
|
// 记录内容
|
||||||
|
if (gameLog.isEmpty()) {
|
||||||
|
Text("暂无记录", color = dimText, fontSize = 13.sp)
|
||||||
|
} else {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.height(450.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
// 滚动内容(纯文字区域)
|
||||||
|
LazyColumn(
|
||||||
|
state = listState,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
itemsIndexed(gameLog) { index, msg ->
|
||||||
|
val playerName = gameLogPlayers.getOrElse(index) { parsePlayerName(msg) }
|
||||||
|
val avatar = getBotAvatar(playerName)
|
||||||
|
val colorText = parseColorText(msg)
|
||||||
|
val cardColor = cardColorMap[colorText]
|
||||||
|
val isPlay = " 出了 " in msg
|
||||||
|
val arrowColor = parseArrowColor(msg)
|
||||||
|
val afterPlayer = msg.substringAfter(playerName)
|
||||||
|
|
||||||
|
val annotated = buildAnnotatedString {
|
||||||
|
withStyle(SpanStyle(color = Color(0xFF999999), fontSize = 13.sp)) {
|
||||||
|
append("${gameLog.size - index}. ")
|
||||||
|
}
|
||||||
|
withStyle(SpanStyle(color = avatar.color, fontWeight = FontWeight.Bold, fontSize = 13.sp)) {
|
||||||
|
append(playerName)
|
||||||
|
}
|
||||||
|
if (isPlay && (colorText.isNotEmpty() || arrowColor != null)) {
|
||||||
|
if (arrowColor != null) {
|
||||||
|
val prefix = afterPlayer.substringBefore(" 万能")
|
||||||
|
val cardBlock = afterPlayer.substringAfter(" 出了 ").substringBefore(",")
|
||||||
|
val remain = afterPlayer.substringAfter(prefix + " 出了 " + cardBlock)
|
||||||
|
withStyle(SpanStyle(color = dimText, fontSize = 13.sp)) {
|
||||||
|
append(prefix); append(" 出了 ")
|
||||||
|
}
|
||||||
|
withStyle(SpanStyle(color = Color.Black, fontWeight = FontWeight.Black,
|
||||||
|
fontSize = 13.sp, background = arrowColor.second.copy(alpha = 0.9f))) {
|
||||||
|
append(cardBlock)
|
||||||
|
}
|
||||||
|
if (remain.isNotEmpty()) {
|
||||||
|
withStyle(SpanStyle(color = Color(0xFF777777), fontSize = 12.sp)) {
|
||||||
|
append(remain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val prefix = afterPlayer.substringBefore(colorText)
|
||||||
|
val cardId = colorText + afterPlayer.substringAfter(colorText).substringBefore(",")
|
||||||
|
val remain = afterPlayer.substringAfter(prefix + cardId)
|
||||||
|
withStyle(SpanStyle(color = dimText, fontSize = 13.sp)) {
|
||||||
|
append(prefix)
|
||||||
|
}
|
||||||
|
withStyle(SpanStyle(color = Color.Black, fontWeight = FontWeight.Black,
|
||||||
|
fontSize = 13.sp, background = (cardColor ?: Color.Gray).copy(alpha = 0.9f))) {
|
||||||
|
append(cardId)
|
||||||
|
}
|
||||||
|
if (remain.isNotEmpty()) {
|
||||||
|
withStyle(SpanStyle(color = Color(0xFF777777), fontSize = 12.sp)) {
|
||||||
|
append(remain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
withStyle(SpanStyle(color = dimText, fontSize = 13.sp)) {
|
||||||
|
append(afterPlayer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text(annotated, modifier = Modifier.padding(vertical = 5.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Box 结束
|
||||||
|
}
|
||||||
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(onClick = { showLogDialog = false }) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Text("关闭", color = Color(0xFF333333))
|
if (canScrollForward) {
|
||||||
|
var bottomPressed by remember { mutableStateOf(false) }
|
||||||
|
val bottomScale by animateFloatAsState(if (bottomPressed) 1.4f else 1f, spring())
|
||||||
|
if (bottomPressed) {
|
||||||
|
LaunchedEffect(Unit) { kotlinx.coroutines.delay(400); bottomPressed = false }
|
||||||
|
}
|
||||||
|
IconButton(
|
||||||
|
onClick = { bottomPressed = true; scope.launch { listState.animateScrollToItem(gameLog.size - 1) } },
|
||||||
|
modifier = Modifier.size(28.dp).scale(bottomScale)
|
||||||
|
) { Icon(Icons.Default.KeyboardArrowDown, "到底部", tint = Color(0xFF555555)) }
|
||||||
|
} else {
|
||||||
|
Spacer(modifier = Modifier.size(28.dp))
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
TextButton(onClick = { showLogDialog = false }) {
|
||||||
|
Text("关闭", color = Color(0xFF333333))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
containerColor = creamBg
|
containerColor = creamBg
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user