Merge pull request #98 from Kevinlinpr/nagisa

修复星座和mbti选择界面上滑后会一直抖动、调整星座和mbti选择界面、、修复短视频在播放时退出后再进入会暂停但是不会显示暂停图标
This commit is contained in:
2025-12-02 12:00:06 +08:00
committed by GitHub
6 changed files with 357 additions and 338 deletions

View File

@@ -20,11 +20,13 @@ import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -47,6 +49,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.ravenow.AppState import com.aiosman.ravenow.AppState
@@ -104,206 +107,202 @@ fun MbtiSelectBottomSheet(
} else { } else {
Color(0xFFFFFFFF) Color(0xFFFFFFFF)
} }
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
// 确保弹窗展开
LaunchedEffect(Unit) {
sheetState.expand()
}
// 监听状态变化,确保弹窗始终展开(防止拖拽关闭和滑动)
LaunchedEffect(sheetState.currentValue, sheetState.targetValue, sheetState.isVisible) {
// 如果弹窗被拖拽关闭或位置发生变化,立即重新展开
if (!sheetState.isVisible || sheetState.targetValue != androidx.compose.material3.SheetValue.Expanded) {
kotlinx.coroutines.delay(10) // 短暂延迟确保状态更新
sheetState.expand()
}
}
val statusBarPadding = WindowInsets.systemBars.asPaddingValues() val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val configuration = LocalConfiguration.current
val screenHeight = configuration.screenHeightDp.dp
val offsetY = screenHeight * 0.07f - statusBarPadding.calculateTopPadding()
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = onClose, onDismissRequest = onClose,
sheetState = sheetState, sheetState = sheetState,
containerColor = sheetBackgroundColor, containerColor = Color.Transparent,
dragHandle = null shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
dragHandle = {}
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.fillMaxHeight(0.95f) .fillMaxHeight()
.offset(y = offsetY) .padding(top = 8.dp)
.padding(
start = 16.dp,
end = 16.dp,
bottom = 8.dp
)
) { ) {
Column( Surface(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.fillMaxHeight() .fillMaxHeight()
.clip(RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)),
color = sheetBackgroundColor,
tonalElevation = 0.dp,
shadowElevation = 0.dp,
) { ) {
// 头部 - 使用 Box 实现绝对居中布局 Column(
Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(48.dp), .fillMaxHeight()
contentAlignment = Alignment.Center .padding(horizontal = 16.dp, vertical = 8.dp)
) { ) {
val cancelButtonGradientColors = if (isDarkMode) { // 头部
listOf( Box(
Color(0xFF3A3A3C),
Color(0xFF2C2C2E)
)
} else {
listOf(
Color(0xFFFFFFFF),
Color(0xFFF8F8F8)
)
}
val cancelButtonContentColor = if (isDarkMode) Color(0xFFFFFFFF) else Color(0xFF404040)
// 左上角返回按钮
Row(
modifier = Modifier modifier = Modifier
.align(Alignment.CenterStart) .fillMaxWidth()
.height(36.dp) .height(48.dp),
.clip(RoundedCornerShape(18.dp)) contentAlignment = Alignment.Center
.background(
brush = Brush.linearGradient(
colors = cancelButtonGradientColors
)
)
.noRippleClickable { onClose() }
.padding(horizontal = 8.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) { ) {
// 左箭头图标 val cancelButtonGradientColors = if (isDarkMode) {
Image( listOf(
painter = painterResource(id = R.drawable.rider_pro_back_icon), Color(0xFF3A3A3C),
contentDescription = null, Color(0xFF2C2C2E)
modifier = Modifier.size(17.dp), )
colorFilter = ColorFilter.tint(cancelButtonContentColor) } else {
) listOf(
Color(0xFFFFFFFF),
// "取消" 文字 Color(0xFFF8F8F8)
Text( )
text = "取消",
fontSize = 17.sp,
fontWeight = FontWeight.Medium,
color = cancelButtonContentColor,
textAlign = androidx.compose.ui.text.style.TextAlign.Center
)
}
// 中间标题 - 绝对居中
Text(
text = stringResource(R.string.choose_mbti),
color = appColors.text,
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
textAlign = androidx.compose.ui.text.style.TextAlign.Center
)
}
Spacer(Modifier.height(12.dp))
// 创建 NestedScrollConnection 来阻止滚动事件向上传播到 ModalBottomSheet
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
// 不消费任何事件,让 LazyColumn 先处理
return Offset.Zero
} }
val cancelButtonContentColor = if (isDarkMode) Color(0xFFFFFFFF) else Color(0xFF404040)
override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset { // 左上角返回按钮:整体「箭头 + 取消」在按钮内居中
// 消费 LazyColumn 处理后的剩余滚动事件,防止传递到 ModalBottomSheet
return available
}
override suspend fun onPreFling(available: Velocity): Velocity {
// 不消费惯性滚动,让 LazyColumn 先处理
return Velocity.Zero
}
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
// 消费 LazyColumn 处理后的剩余惯性滚动,防止传递到 ModalBottomSheet
return available
}
}
}
// MBTI解释文字背景色
val descriptionBackgroundColor = if (isDarkMode) {
Color(0xFF2A2A2A) // 比 secondaryBackground (0xFF1C1C1C) 更亮的灰色
} else {
Color(0xFFFAF9FB)
}
// 使用LazyColumn包裹解释文字和MBTI类型网格使它们一起滚动
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.nestedScroll(nestedScrollConnection),
contentPadding = PaddingValues(
start = 8.dp,
top = 0.dp,
end = 8.dp,
bottom = 8.dp
),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// MBTI解释文字 - 作为第一个item
item {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .align(Alignment.CenterStart)
.clip(RoundedCornerShape(16.dp)) .width(91.dp)
.background(descriptionBackgroundColor) .height(44.dp)
.padding(horizontal = 16.dp, vertical = 12.dp) .clip(RoundedCornerShape(1000.dp))
.background(
brush = Brush.linearGradient(
colors = cancelButtonGradientColors
)
)
.noRippleClickable { onClose() },
contentAlignment = Alignment.Center
) { ) {
Text( Row(
text = stringResource(R.string.mbti_description), modifier = Modifier
color = appColors.text, .padding(horizontal = 12.dp, vertical = 4.dp),
fontSize = 14.sp, horizontalArrangement = Arrangement.Center,
lineHeight = 20.sp verticalAlignment = Alignment.CenterVertically
) ) {
Image(
painter = painterResource(id = R.drawable.rider_pro_back_icon),
contentDescription = null,
modifier = Modifier.size(17.dp),
colorFilter = ColorFilter.tint(cancelButtonContentColor)
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = "取消",
fontSize = 17.sp,
fontWeight = FontWeight.Medium,
color = cancelButtonContentColor,
textAlign = TextAlign.Center,
maxLines = 1,
overflow = TextOverflow.Clip
)
}
}
// 中间标题
Text(
text = stringResource(R.string.choose_mbti),
color = appColors.text,
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center
)
}
Spacer(Modifier.height(12.dp))
// NestedScroll阻止滚动事件向上传到 BottomSheet
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
return Offset.Zero
}
override fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource
): Offset {
return available
}
override suspend fun onPreFling(available: Velocity): Velocity {
return Velocity.Zero
}
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
return available
}
} }
} }
// MBTI类型网格 - 手动创建2列网格布局 val descriptionBackgroundColor = if (isDarkMode) {
itemsIndexed(MBTI_TYPES.chunked(2)) { rowIndex, rowItems -> Color(0xFF2A2A2A)
Row( } else {
modifier = Modifier Color(0xFFFAF9FB)
.fillMaxWidth() }
.padding(bottom = if (rowIndex < MBTI_TYPES.chunked(2).size - 1) 10.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp) // 列表:上面是说明文字,下面是 MBTI 网格
) { LazyColumn(
rowItems.forEachIndexed { colIndex, mbti -> modifier = Modifier
Box( .fillMaxWidth()
modifier = Modifier.weight(1f) .weight(1f)
) { .nestedScroll(nestedScrollConnection),
MbtiItem( contentPadding = PaddingValues(
mbti = mbti, start = 8.dp,
isSelected = mbti == currentMbti, top = 0.dp,
onClick = { end = 8.dp,
// 保存MBTI类型 bottom = 8.dp
model.mbti = mbti ),
onClose() verticalArrangement = Arrangement.spacedBy(16.dp)
} ) {
) // 说明文字
} item {
Box(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(16.dp))
.background(descriptionBackgroundColor)
.padding(horizontal = 16.dp, vertical = 12.dp)
) {
Text(
text = stringResource(R.string.mbti_description),
color = appColors.text,
fontSize = 14.sp,
lineHeight = 20.sp
)
} }
// 如果这一行只有1个item添加一个空的Spacer来保持布局 }
if (rowItems.size == 1) {
Spacer(modifier = Modifier.weight(1f)) // MBTI 类型网格2 列)
itemsIndexed(MBTI_TYPES.chunked(2)) { rowIndex, rowItems ->
Row(
modifier = Modifier
.fillMaxWidth()
.padding(
bottom = if (rowIndex < MBTI_TYPES.chunked(2).size - 1) 10.dp else 0.dp
),
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
rowItems.forEach { mbti ->
Box(
modifier = Modifier.weight(1f)
) {
MbtiItem(
mbti = mbti,
isSelected = mbti == currentMbti,
onClick = {
model.mbti = mbti
onClose()
}
)
}
}
if (rowItems.size == 1) {
Spacer(modifier = Modifier.weight(1f))
}
} }
} }
} }
@@ -358,30 +357,26 @@ fun MbtiItem(
) { ) {
onClick() onClick()
} }
.padding(horizontal = 24.dp, vertical = 12.dp), .padding(horizontal = 16.dp, vertical = 12.dp),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center verticalArrangement = Arrangement.Center
) { ) {
// MBTI图标 - 使用占位图片 // 直接把 MBTI 图标和文字放在灰色卡片内部,布局与星座保持一致
Box( Image(
modifier = Modifier.size(100.dp), painter = painterResource(id = getMbtiImageResId(mbti, isDarkMode)),
contentAlignment = Alignment.Center contentDescription = mbti,
) { modifier = Modifier.size(96.dp)
Image( )
painter = painterResource(id = getMbtiImageResId(mbti, isDarkMode)),
contentDescription = mbti, Spacer(modifier = Modifier.height(0.dp))
modifier = Modifier.size(100.dp)
)
}
// MBTI名称 - 使用负间距让文本向上移动,与图标更靠近
Text( Text(
text = mbti, text = mbti,
fontSize = 14.sp, fontSize = 14.sp,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = appColors.text, color = appColors.text,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.offset(y = (-20).dp) modifier = Modifier.offset(y = (-10).dp)
) )
} }
} }

View File

@@ -28,6 +28,7 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -47,6 +48,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -134,168 +136,165 @@ fun ZodiacSelectBottomSheet(
} }
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
// 确保弹窗展开
LaunchedEffect(Unit) {
sheetState.expand()
}
// 监听状态变化,确保弹窗始终展开(防止拖拽关闭和滑动)
LaunchedEffect(sheetState.currentValue, sheetState.targetValue, sheetState.isVisible) {
// 如果弹窗被拖拽关闭或位置发生变化,立即重新展开
if (!sheetState.isVisible || sheetState.targetValue != androidx.compose.material3.SheetValue.Expanded) {
kotlinx.coroutines.delay(10) // 短暂延迟确保状态更新
sheetState.expand()
}
}
val statusBarPadding = WindowInsets.systemBars.asPaddingValues()
val configuration = LocalConfiguration.current
val screenHeight = configuration.screenHeightDp.dp
val offsetY = screenHeight * 0.07f - statusBarPadding.calculateTopPadding()
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = onClose, onDismissRequest = onClose,
sheetState = sheetState, sheetState = sheetState,
containerColor = sheetBackgroundColor, // 根据主题自适应背景 // 对齐发布动态草稿箱样式:底层透明,内容区域自己绘制圆角和背景
dragHandle = null containerColor = Color.Transparent,
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
dragHandle = {}
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.fillMaxHeight(0.95f) .fillMaxHeight()
.offset(y = offsetY) .padding(top = 8.dp)
.padding(
start = 16.dp,
end = 16.dp,
bottom = 8.dp
)
) { ) {
Column( Surface(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.fillMaxHeight() .fillMaxHeight()
.clip(RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)),
color = sheetBackgroundColor,
tonalElevation = 0.dp,
shadowElevation = 0.dp,
) { ) {
// 头部 - 使用 Box 实现绝对居中布局 Column(
Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(48.dp), .fillMaxHeight()
contentAlignment = Alignment.Center .padding(horizontal = 16.dp, vertical = 8.dp)
) { ) {
val cancelButtonGradientColors = if (isDarkMode) { // 头部 - 使用 Box 实现绝对居中布局(对齐草稿箱样式)
listOf( Box(
Color(0xFF3A3A3C),
Color(0xFF2C2C2E)
)
} else {
listOf(
Color(0xFFFFFFFF),
Color(0xFFF8F8F8)
)
}
val cancelButtonContentColor = if (isDarkMode) Color(0xFFFFFFFF) else Color(0xFF404040)
// 左上角返回按钮 - 根据 Swift 代码样式,带淡灰色渐变背景
Row(
modifier = Modifier modifier = Modifier
.align(Alignment.CenterStart) .fillMaxWidth()
.height(36.dp) .height(48.dp),
.clip(RoundedCornerShape(18.dp)) // 圆角 100.0 在 36dp 高度下接近完全圆角 contentAlignment = Alignment.Center
.background(
brush = Brush.linearGradient(
colors = cancelButtonGradientColors
// 不指定 start 和 end默认从左上到右下
)
)
.noRippleClickable { onClose() }
.padding(horizontal = 8.dp), // 内部 padding 确保内容不贴边
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) { ) {
// 左箭头图标 val cancelButtonGradientColors = if (isDarkMode) {
Image( listOf(
painter = painterResource(id = R.drawable.rider_pro_back_icon), Color(0xFF3A3A3C),
contentDescription = null, Color(0xFF2C2C2E)
modifier = Modifier.size(17.dp), )
colorFilter = ColorFilter.tint(cancelButtonContentColor) } else {
) listOf(
Color(0xFFFFFFFF),
Color(0xFFF8F8F8)
)
}
val cancelButtonContentColor = if (isDarkMode) Color(0xFFFFFFFF) else Color(0xFF404040)
// 左上角返回按钮:参考 iOS 设计,整体「箭头 + 取消」在 91x44 的按钮内居中
Box(
modifier = Modifier
.align(Alignment.CenterStart)
.width(91.dp)
.height(44.dp)
.clip(RoundedCornerShape(1000.dp))
.background(
brush = Brush.linearGradient(
colors = cancelButtonGradientColors
)
)
.noRippleClickable { onClose() },
contentAlignment = Alignment.Center
) {
Row(
modifier = Modifier
// 不再固定宽度,让内容自然占位,避免裁剪掉“消”字
.padding(horizontal = 12.dp, vertical = 4.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(id = R.drawable.rider_pro_back_icon),
contentDescription = null,
modifier = Modifier.size(17.dp),
colorFilter = ColorFilter.tint(cancelButtonContentColor)
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = "取消",
fontSize = 17.sp,
fontWeight = FontWeight.Medium,
color = cancelButtonContentColor,
textAlign = androidx.compose.ui.text.style.TextAlign.Center,
maxLines = 1,
overflow = TextOverflow.Clip
)
}
}
// "取消" 文字
Text( Text(
text = "取消", text = stringResource(R.string.choose_zodiac),
fontSize = 17.sp, color = appColors.text,
fontWeight = FontWeight.Medium, fontSize = 20.sp,
color = cancelButtonContentColor, fontWeight = FontWeight.Bold,
textAlign = androidx.compose.ui.text.style.TextAlign.Center textAlign = androidx.compose.ui.text.style.TextAlign.Center
) )
} }
// 中间标题 - 绝对居中
Text(
text = stringResource(R.string.choose_zodiac),
color = appColors.text,
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
textAlign = androidx.compose.ui.text.style.TextAlign.Center
)
}
Spacer(Modifier.height(12.dp)) Spacer(Modifier.height(12.dp))
// 创建 NestedScrollConnection 来阻止滚动事件向上传播到 ModalBottomSheet // 创建 NestedScrollConnection
val nestedScrollConnection = remember { // 1. 不抢在列表前面消费事件,让 LazyVerticalGrid 正常滚动
object : NestedScrollConnection { // 2. 在列表滚动之后把剩余滚动吃掉,避免继续传递到 BottomSheet 去触发下拉关闭
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { val nestedScrollConnection = remember {
// 不消费任何事件,让 LazyVerticalGrid 先处理 object : NestedScrollConnection {
return Offset.Zero override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
} // 不在这里消费,先让 LazyVerticalGrid 自己处理滚动
return Offset.Zero
}
override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset { override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset {
// 消费 LazyVerticalGrid 处理后的剩余滚动事件,防止传递到 ModalBottomSheet // 列表滚动完之后,把剩余滚动(尤其是向下拖拽)全部吃掉,防止再传给 BottomSheet
return available return available
} }
override suspend fun onPreFling(available: Velocity): Velocity { override suspend fun onPreFling(available: Velocity): Velocity {
// 不消费惯性滚动,让 LazyVerticalGrid 先处理 // 不抢在列表前面处理 fling,让 LazyVerticalGrid 先做惯性滚动
return Velocity.Zero return Velocity.Zero
} }
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
// 消费 LazyVerticalGrid 处理后的剩余惯性滚动,防止传递到 ModalBottomSheet // 列表惯性滚动之后,把剩余的 fling 速度吃掉,避免带动 BottomSheet 下滑关闭
return available return available
}
} }
} }
}
// 网格列表 - 2列 // 网格列表 - 2列(与草稿箱一样放在内容区域内部滚动)
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(2), columns = GridCells.Fixed(2),
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.weight(1f) .weight(1f)
.nestedScroll(nestedScrollConnection), .nestedScroll(nestedScrollConnection),
contentPadding = PaddingValues( contentPadding = PaddingValues(
start = 8.dp, start = 8.dp,
top = 8.dp, top = 8.dp,
end = 8.dp, end = 8.dp,
bottom = 8.dp bottom = 8.dp
), ),
horizontalArrangement = Arrangement.spacedBy(10.dp), horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalArrangement = Arrangement.spacedBy(10.dp) verticalArrangement = Arrangement.spacedBy(10.dp)
) { ) {
itemsIndexed(ZODIAC_SIGN_RES_IDS) { index, zodiacResId -> itemsIndexed(ZODIAC_SIGN_RES_IDS) { index, zodiacResId ->
val zodiacText = stringResource(zodiacResId) val zodiacText = stringResource(zodiacResId)
ZodiacItem( ZodiacItem(
zodiac = zodiacText, zodiac = zodiacText,
zodiacResId = zodiacResId, zodiacResId = zodiacResId,
isSelected = zodiacResId == currentZodiacResId, isSelected = zodiacResId == currentZodiacResId,
onClick = { onClick = {
// 保存当前语言的星座文本 model.zodiac = zodiacText
model.zodiac = zodiacText onClose()
onClose() }
} )
) }
} }
} }
} }
@@ -336,9 +335,9 @@ fun ZodiacItem(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.aspectRatio(1.1f) // 增加宽高比,使高度相对更低 .aspectRatio(1.1f)
.shadow( .shadow(
elevation = if (isDarkMode) 8.dp else 2.dp, // 深色模式下更强的阴影 elevation = if (isDarkMode) 8.dp else 2.dp,
shape = RoundedCornerShape(21.dp), shape = RoundedCornerShape(21.dp),
spotColor = if (isDarkMode) Color.Black.copy(alpha = 0.5f) else Color.Black.copy(alpha = 0.1f) spotColor = if (isDarkMode) Color.Black.copy(alpha = 0.5f) else Color.Black.copy(alpha = 0.1f)
) )
@@ -350,30 +349,27 @@ fun ZodiacItem(
) { ) {
onClick() onClick()
} }
.padding(horizontal = 24.dp, vertical = 12.dp), // 减小垂直padding确保文本不被遮挡 .padding(horizontal = 16.dp, vertical = 12.dp),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center verticalArrangement = Arrangement.Center
) { ) {
// 星座图标 - 使用对应星座的图片 // 直接把图标和文字放在灰色卡片内部,不再额外嵌套一层 Box
Box( Image(
modifier = Modifier.size(100.dp), painter = painterResource(id = getZodiacImageResId(zodiacResId)),
contentAlignment = Alignment.Center contentDescription = zodiac,
) { // 图标稍微放大一些,让视觉更聚焦在星座图标上
Image( modifier = Modifier.size(96.dp)
painter = painterResource(id = getZodiacImageResId(zodiacResId)), )
contentDescription = zodiac,
modifier = Modifier.size(100.dp) Spacer(modifier = Modifier.height(0.dp))
)
}
// 星座名称 - 使用负间距让文本向上移动,与图标更靠近
Text( Text(
text = zodiac, text = zodiac,
fontSize = 14.sp, fontSize = 14.sp,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = appColors.text, color = appColors.text,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.offset(y = (-20).dp) // 负间距,让文本进一步向上移动 modifier = Modifier.offset(y = (-10).dp) // 再整体向上偏移 5dp共 10dp
) )
} }
} }

View File

@@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
@@ -29,6 +30,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -40,6 +42,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@@ -107,6 +110,8 @@ fun CommentModalContent(
var softwareKeyboardController = LocalSoftwareKeyboardController.current var softwareKeyboardController = LocalSoftwareKeyboardController.current
var replyComment by remember { mutableStateOf<CommentEntity?>(null) } var replyComment by remember { mutableStateOf<CommentEntity?>(null) }
var shouldAutoFocus by remember { mutableStateOf(false) } var shouldAutoFocus by remember { mutableStateOf(false) }
val listState = rememberLazyListState()
val scope = rememberCoroutineScope()
LaunchedEffect(imePadding) { LaunchedEffect(imePadding) {
bottomPadding = imePadding.dp bottomPadding = imePadding.dp
@@ -213,6 +218,9 @@ fun CommentModalContent(
) { ) {
commentViewModel.order = it commentViewModel.order = it
commentViewModel.reloadComment() commentViewModel.reloadComment()
scope.launch {
listState.scrollToItem(0)
}
} }
} }
Box( Box(
@@ -223,7 +231,8 @@ fun CommentModalContent(
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp),
state = listState
) { ) {
item { item {
CommentContent( CommentContent(

View File

@@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
@@ -30,6 +31,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -144,6 +146,8 @@ fun NewsCommentModal(
var showCommentMenu by remember { mutableStateOf(false) } var showCommentMenu by remember { mutableStateOf(false) }
var contextComment by remember { mutableStateOf<CommentEntity?>(null) } var contextComment by remember { mutableStateOf<CommentEntity?>(null) }
var replyComment by remember { mutableStateOf<CommentEntity?>(null) } var replyComment by remember { mutableStateOf<CommentEntity?>(null) }
val listState = rememberLazyListState()
val scope = rememberCoroutineScope()
// 菜单弹窗 // 菜单弹窗
if (showCommentMenu) { if (showCommentMenu) {
@@ -231,6 +235,9 @@ fun NewsCommentModal(
) { ) {
commentViewModel.order = it commentViewModel.order = it
commentViewModel.reloadComment() commentViewModel.reloadComment()
scope.launch {
listState.scrollToItem(0)
}
} }
} }
} }
@@ -269,7 +276,9 @@ fun NewsCommentModal(
) )
} }
} else { } else {
LazyColumn { LazyColumn(
state = listState
) {
item { item {
CommentContent( CommentContent(
viewModel = commentViewModel, viewModel = commentViewModel,

View File

@@ -347,6 +347,11 @@ fun VideoRecommendationItem(
if (isVisible && shouldResumeAfterLifecyclePause) { if (isVisible && shouldResumeAfterLifecyclePause) {
pauseIconVisibleState = false pauseIconVisibleState = false
exoPlayer.play() exoPlayer.play()
} else {
// 未自动恢复播放时,如果当前可见且视频已暂停,则显示暂停图标
if (isVisible && !exoPlayer.isPlaying) {
pauseIconVisibleState = true
}
} }
} }
else -> {} else -> {}

View File

@@ -549,6 +549,11 @@ fun VideoPlayer(
exoPlayer.playWhenReady = true exoPlayer.playWhenReady = true
exoPlayer.play() exoPlayer.play()
pauseIconVisibleState.value = false pauseIconVisibleState.value = false
} else {
// 未自动恢复播放时,如果当前页面视频处于暂停状态,则显示暂停图标
if (!exoPlayer.isPlaying) {
pauseIconVisibleState.value = true
}
} }
} }