优化智能体(Agent)展示轮播

- 将原有的分页网格布局,重构为全屏卡片式轮播。
- 每个智能体以带有背景大图的卡片展示,增强视觉效果。
- 在卡片底部增加了渐变遮罩、标题和描述,并在底部中央添加了“聊天”按钮,以改善用户交互和界面美观度。
- 调整了轮播的尺寸和间距,使其自适应屏幕宽度。
This commit is contained in:
2025-11-10 23:34:35 +08:00
parent 1b70cb5cdb
commit 2f41c61b7e

View File

@@ -88,6 +88,10 @@ import androidx.compose.ui.window.DialogProperties
import androidx.compose.foundation.lazy.grid.items as gridItems import androidx.compose.foundation.lazy.grid.items as gridItems
import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.CircularProgressIndicator
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Brush
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.ui.platform.LocalConfiguration
// 检测是否接近列表底部的扩展函数 // 检测是否接近列表底部的扩展函数
fun LazyListState.isScrolledToEnd(buffer: Int = 3): Boolean { fun LazyListState.isScrolledToEnd(buffer: Int = 3): Boolean {
@@ -581,73 +585,147 @@ fun AgentCardSquare(
@Composable @Composable
fun AgentViewPagerSection(agentItems: List<AgentItem>,viewModel: AgentViewModel) { fun AgentViewPagerSection(agentItems: List<AgentItem>,viewModel: AgentViewModel) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
if (agentItems.isEmpty()) return
// 每页显示5个agent val pagerState = rememberPagerState(pageCount = { agentItems.size })
val itemsPerPage = 5 val screenWidth = LocalConfiguration.current.screenWidthDp.dp
val totalPages = (agentItems.size + itemsPerPage - 1) / itemsPerPage val cardAspect = 1133.5f / 846.4f
// 外层 LazyColumn 左右各 8dp + Pager contentPadding 左右各 20dp
val horizontalPaddings = 56.dp
val pagerHeight = (screenWidth - horizontalPaddings) * cardAspect
if (totalPages > 0) { Column {
val pagerState = rememberPagerState(pageCount = { totalPages }) Box(
modifier = Modifier
.height(pagerHeight)
) {
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxSize(),
contentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 20.dp),
pageSpacing = 12.dp
) { page ->
// 缩放效果
val pageOffset = (
(pagerState.currentPage - page) + pagerState
.currentPageOffsetFraction
).coerceIn(-1f, 1f)
val scale = 1f - (0.06f * kotlin.math.abs(pageOffset))
Column { AgentLargeCard(
// Agent内容 agentItem = agentItems[page],
Box( viewModel = viewModel,
modifier = Modifier navController = LocalNavController.current,
.height(310.dp) modifier = Modifier
) { .graphicsLayer {
HorizontalPager( scaleX = scale
state = pagerState, scaleY = scale
modifier = Modifier.fillMaxSize(), }
contentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 4.dp), )
pageSpacing = 0.dp }
) { page -> }
// 计算当前页面的偏移量 }
val pageOffset = ( }
(pagerState.currentPage - page) + pagerState
.currentPageOffsetFraction
).coerceIn(-1f, 1f)
// 根据偏移量计算缩放比例 @SuppressLint("SuspiciousIndentation")
val scale = 1f - (0.1f * kotlin.math.abs(pageOffset)) @Composable
fun AgentLargeCard(
agentItem: AgentItem,
viewModel: AgentViewModel,
navController: NavHostController,
modifier: Modifier = Modifier
) {
val AppColors = LocalAppTheme.current
var lastClickTime by remember { mutableStateOf(0L) }
AgentPage( Box(
viewModel = viewModel, modifier = modifier
agentItems = agentItems.drop(page * itemsPerPage).take(itemsPerPage), .fillMaxWidth()
page = page, .aspectRatio(846.4f / 1133.5f)
modifier = Modifier .clip(RoundedCornerShape(24.dp))
.height(310.dp) .noRippleClickable {
.graphicsLayer { if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
scaleX = scale viewModel.goToProfile(agentItem.openId, navController)
scaleY = scale }) {
}, lastClickTime = System.currentTimeMillis()
navController = LocalNavController.current,
)
} }
} }
) {
// 背景大图
CustomAsyncImage(
imageUrl = agentItem.avatar,
contentDescription = agentItem.title,
modifier = Modifier.fillMaxSize(),
contentScale = androidx.compose.ui.layout.ContentScale.Crop
)
// 指示器 // 底部渐变与文字
Row( Box(
modifier = Modifier
.align(Alignment.BottomStart)
.fillMaxWidth()
.background(
Brush.verticalGradient(
0f to Color.Transparent,
1f to Color(0xB2000000)
)
)
.padding(16.dp)
) {
Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(30.dp) .padding(bottom = 56.dp) // 为底部聊天按钮预留空间
.padding(top = 12.dp),
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.Center
) { ) {
repeat(totalPages) { index -> androidx.compose.material3.Text(
Box( text = agentItem.title,
modifier = Modifier color = Color.White,
.padding(horizontal = 4.dp) fontSize = 20.sp,
.size(3.dp) fontWeight = androidx.compose.ui.text.font.FontWeight.W700,
.background( maxLines = 1,
color = if (pagerState.currentPage == index) AppColors.text else AppColors.secondaryText.copy( overflow = TextOverflow.Ellipsis
alpha = 0.3f )
), Spacer(modifier = Modifier.height(8.dp))
shape = androidx.compose.foundation.shape.CircleShape androidx.compose.material3.Text(
) text = agentItem.desc,
) color = Color.White.copy(alpha = 0.92f),
} fontSize = 14.sp,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
} }
} }
// 底部居中 Chat 按钮
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 16.dp)
.widthIn(min = 180.dp)
.fillMaxWidth(0.65f)
.height(44.dp)
.background(AppColors.text, RoundedCornerShape(22.dp))
.noRippleClickable {
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CHAT_WITH_AGENT)) {
navController.navigate(NavigationRoute.Login.route)
} else {
viewModel.createSingleChat(agentItem.openId)
viewModel.goToChatAi(agentItem.openId, navController)
}
}) {
lastClickTime = System.currentTimeMillis()
}
},
contentAlignment = Alignment.Center
) {
androidx.compose.material3.Text(
text = stringResource(R.string.chat),
color = AppColors.background,
fontSize = 16.sp,
fontWeight = androidx.compose.ui.text.font.FontWeight.W600
)
}
} }
} }