优化智能体(Agent)展示轮播
- 将原有的分页网格布局,重构为全屏卡片式轮播。 - 每个智能体以带有背景大图的卡片展示,增强视觉效果。 - 在卡片底部增加了渐变遮罩、标题和描述,并在底部中央添加了“聊天”按钮,以改善用户交互和界面美观度。 - 调整了轮播的尺寸和间距,使其自适应屏幕宽度。
This commit is contained in:
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user