From 96d804b4c7902b14909c6ad6cc8c8bf61ac6e48d Mon Sep 17 00:00:00 2001 From: AllenTom Date: Thu, 13 Nov 2025 16:15:40 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=AA=E4=BA=BA?= =?UTF-8?q?=E4=B8=BB=E9=A1=B5=E7=9A=84=E5=AF=BC=E8=88=AA=E6=A0=8F=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E5=92=8C=E8=A7=86=E8=A7=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 优化导航栏的透明度过渡效果,使其在滚动时更早达到完全不透明,并适配深色/亮色模式。 - 调整导航栏图标和边框颜色逻辑,使其在深色模式下始终为白色,在亮色模式下根据背景透明度在白色和黑色之间切换。 - 将互动数据卡片和分享按钮调整为仅在“我的”主页显示。 - 将页面内容(`HorizontalPager`)的高度从 `500.dp` 增加到 `650.dp`。 - 更新了导航栏右侧的菜单图标。 --- .../ui/index/tabs/profile/ProfileV3.kt | 226 +++++++++--------- 1 file changed, 107 insertions(+), 119 deletions(-) diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/ProfileV3.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/ProfileV3.kt index d70e6cc..8734bae 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/ProfileV3.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/profile/ProfileV3.kt @@ -4,9 +4,11 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.util.Log +import androidx.compose.foundation.Canvas import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -14,7 +16,6 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.asPaddingValues -import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -22,12 +23,11 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll @@ -37,12 +37,12 @@ import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -52,26 +52,24 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.drawWithContent -import androidx.compose.ui.draw.shadow +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.graphics.nativeCanvas +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource -import androidx.compose.runtime.collectAsState +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +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.sp import com.aiosman.ravenow.AppState import com.aiosman.ravenow.ConstVars -import com.aiosman.ravenow.GuestLoginCheckOut -import com.aiosman.ravenow.GuestLoginCheckOutScene import com.aiosman.ravenow.LocalAppTheme import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.MainActivity @@ -82,14 +80,9 @@ import com.aiosman.ravenow.entity.AgentEntity import com.aiosman.ravenow.entity.MomentEntity import com.aiosman.ravenow.ui.NavigationRoute import com.aiosman.ravenow.ui.composables.CustomAsyncImage -import com.aiosman.ravenow.ui.composables.StatusBarSpacer import com.aiosman.ravenow.ui.composables.pickupAndCompressLauncher -import com.aiosman.ravenow.ui.composables.toolbar.CollapsingToolbarScaffold -import com.aiosman.ravenow.ui.composables.toolbar.ScrollStrategy -import com.aiosman.ravenow.ui.composables.toolbar.rememberCollapsingToolbarScaffoldState import com.aiosman.ravenow.ui.index.IndexViewModel import com.aiosman.ravenow.ui.index.tabs.profile.composable.GalleryGrid -import com.aiosman.ravenow.ui.post.MenuActionItem import com.aiosman.ravenow.ui.index.tabs.profile.composable.GroupChatEmptyContent import com.aiosman.ravenow.ui.index.tabs.profile.composable.OtherProfileAction import com.aiosman.ravenow.ui.index.tabs.profile.composable.UserAgentsList @@ -97,23 +90,13 @@ import com.aiosman.ravenow.ui.index.tabs.profile.composable.UserAgentsRow import com.aiosman.ravenow.ui.index.tabs.profile.composable.UserContentPageIndicator import com.aiosman.ravenow.ui.index.tabs.profile.composable.UserItem import com.aiosman.ravenow.ui.modifiers.noRippleClickable -import com.aiosman.ravenow.ui.navigateToPost +import com.aiosman.ravenow.ui.post.MenuActionItem import com.google.accompanist.systemuicontroller.rememberSystemUiController import kotlinx.coroutines.delay import kotlinx.coroutines.launch import java.io.File -import androidx.compose.foundation.rememberScrollState -import androidx.compose.ui.res.stringResource -import androidx.compose.foundation.Canvas -import androidx.compose.foundation.border -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.input.nestedscroll.NestedScrollSource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.graphics.Brush import java.text.NumberFormat import java.util.Locale -import com.aiosman.ravenow.ui.points.PointsBottomSheet import kotlin.math.max @OptIn(ExperimentalFoundationApi::class, ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) @@ -233,16 +216,13 @@ fun ProfileV3( } val pointsBalanceState = PointService.pointsBalance.collectAsState(initial = null) - // 计算导航栏背景透明度,根据滚动位置从0到1 + // 计算导航栏背景透明度,根据滚动位置从0到1,在前段就达到完全不透明 val toolbarBackgroundAlpha by remember { derivedStateOf { - if (!isSelf) { - 1f - } else { - val maxScroll = 600f // 增加最大滚动距离,让渐变更平缓 - val progress = (scrollState.value.coerceAtMost(maxScroll.toInt()) / maxScroll).coerceIn(0f, 1f) - progress - } + val maxScroll = 120f // 大幅减少最大滚动距离,让前段就完全不透明 + val progress = (scrollState.value.coerceAtMost(maxScroll.toInt()) / maxScroll).coerceIn(0f, 1f) + // 直接使用线性插值,尽快达到完全不透明 + progress } } @@ -530,7 +510,7 @@ fun ProfileV3( ) HorizontalPager( state = pagerState, - modifier = Modifier.height(500.dp) // 固定滚动高度 + modifier = Modifier.height(650.dp) // 固定滚动高度 ) { idx -> when (idx) { 0 -> GalleryGrid( @@ -715,24 +695,18 @@ fun TopNavigationBar( // 仅本人主页显示积分:收集全局积分 val pointsBalanceState = if (isSelf) PointService.pointsBalance.collectAsState(initial = null) else null - // 根据背景透明度和主题决定图标与边框颜色 - val iconColor = if (backgroundAlpha >= 0.7f) appColors.text else Color.White - val cardBorderColor = if (backgroundAlpha >= 0.7f) appColors.divider else Color.White - val toolbarSolidColor = remember(backgroundAlpha, appColors) { - appColors.background.copy(alpha = backgroundAlpha.coerceIn(0f, 1f)) + // 根据背景透明度和暗色模式决定图标颜色 + // 暗色模式下:图标始终为白色 + // 亮色模式下:根据背景透明度决定,透明度为1时变黑,否则为白色 + val iconColor = if (AppState.darkMode) { + Color.White // 暗色模式下图标始终为白色 + } else { + if (backgroundAlpha >= 1f) Color.Black else Color.White } - val toolbarOverlayBrush = remember(backgroundAlpha) { - val overlayAlpha = (1f - backgroundAlpha).coerceIn(0f, 1f) * 0.25f - if (overlayAlpha > 0f) { - Brush.verticalGradient( - colors = listOf( - Color.Black.copy(alpha = overlayAlpha), - Color.Transparent - ) - ) - } else { - null - } + val cardBorderColor = if (AppState.darkMode) { + Color.White // 暗色模式下边框应为白色 + } else { + if (backgroundAlpha >= 1f) Color.Black else Color.White } Box( @@ -743,25 +717,31 @@ fun TopNavigationBar( val statusBarHeight = statusBarPadding.calculateTopPadding() val navigationBarHeight = 56.dp // 增加导航栏高度,包括图标和额外空间 - // 导航栏背景层,包括状态栏区域,根据滚动位置逐渐变白 + // 导航栏背景层,包括状态栏区域,根据滚动位置逐渐显示实色填充 val totalHeight = statusBarHeight + navigationBarHeight + // 根据滚动位置计算背景颜色,从透明逐渐变为实色填充,尽快完成 + val toolbarBackgroundColor = remember(backgroundAlpha) { + val progress = backgroundAlpha.coerceIn(0f, 1f) + + if (AppState.darkMode) { + // 暗色模式下:从透明逐渐变为黑色实色填充 + Color.Black.copy(alpha = progress) + } else { + // 亮色模式下:从透明逐渐变为白色实色填充 + Color.White.copy(alpha = progress) + } + } + Box( modifier = Modifier .fillMaxWidth() .height(totalHeight) // 状态栏高度 + 导航栏高度 .align(Alignment.TopCenter) - .background(toolbarSolidColor) + .background( + color = toolbarBackgroundColor // 实色填充,不使用渐变 + ) ) - toolbarOverlayBrush?.let { brush -> - Box( - modifier = Modifier - .fillMaxWidth() - .height(totalHeight) - .align(Alignment.TopCenter) - .background(brush) - ) - } // 功能按钮区域,图标和文字根据背景透明度改变颜色 Row( @@ -773,65 +753,73 @@ fun TopNavigationBar( horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.CenterVertically ) { - // 左侧:互动数据卡片 - Row( - modifier = Modifier - .height(24.dp) - .background( - color = Color.White.copy(alpha = 0.52f), - shape = RoundedCornerShape(16.dp) - ) - .border( - width = 0.5.dp, - color = cardBorderColor, // 根据背景透明度改变边框颜色 - shape = RoundedCornerShape(16.dp) - ) - .padding(horizontal = 8.dp) - .let { - if (isSelf) it.noRippleClickable { onPointsClick?.invoke() } else it - }, - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - // 互动图标 - Image( - painter = painterResource(id = R.mipmap.paip_coin_img), - contentDescription = "互动", - modifier = Modifier.size(24.dp), - ) - Spacer(modifier = Modifier.width(4.dp)) - Text( - text = if (isSelf) { - pointsBalanceState?.value?.balance?.let { numberFormat.format(it) } ?: "--" + // 左侧:互动数据卡片(仅自己的界面显示) + if (isSelf) { + // 根据 toolbar 背景透明度动态调整卡片背景 + val cardBackgroundColor = remember(backgroundAlpha) { + val smoothProgress = backgroundAlpha.coerceIn(0f, 1f) + if (AppState.darkMode) { + // 暗色模式:从半透明白色逐渐变为更不透明的白色 + Color.White.copy(alpha = 0.52f + (0.48f * smoothProgress)) } else { - numberFormat.format(interactionCount) - }, - fontSize = 14.sp, - fontWeight = FontWeight.W500, - color = if (AppState.darkMode) Color.White else Color.Black, // 暗色模式下为白色,亮色模式下为黑色 - textAlign = TextAlign.Center + // 亮色模式:从半透明白色逐渐变为完全不透明的白色 + Color.White.copy(alpha = 0.52f + (0.48f * smoothProgress)) + } + } + + Row( + modifier = Modifier + .height(24.dp) + .background( + color = cardBackgroundColor, + shape = RoundedCornerShape(16.dp) + ) + .border( + width = 0.5.dp, + color = cardBorderColor, // 根据背景透明度改变边框颜色 + shape = RoundedCornerShape(16.dp) + ) + .padding(horizontal = 8.dp) + .noRippleClickable { onPointsClick?.invoke() }, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + // 互动图标 + Image( + painter = painterResource(id = R.mipmap.paip_coin_img), + contentDescription = "互动", + modifier = Modifier.size(24.dp), + ) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = pointsBalanceState?.value?.balance?.let { numberFormat.format(it) } ?: "--", + fontSize = 14.sp, + fontWeight = FontWeight.W500, + color = if (AppState.darkMode) Color.White else Color.Black, // 暗色模式下为白色,亮色模式下为黑色 + textAlign = TextAlign.Center + ) + } + + Spacer(modifier = Modifier.width(16.dp)) + + // 中间:分享图标(仅自己的界面显示) + Image( + painter = painterResource(id = R.mipmap.menu_icon), + contentDescription = "分享", + modifier = Modifier + .size(24.dp) + .noRippleClickable { + onShareClick() + }, + colorFilter = ColorFilter.tint(iconColor) // 根据背景透明度改变颜色 ) + + Spacer(modifier = Modifier.width(16.dp)) } - Spacer(modifier = Modifier.width(16.dp)) - - // 中间:分享图标 + // 右侧:菜单图标(三点图标) Image( - painter = painterResource(id = R.mipmap.menu_icon), - contentDescription = "分享", - modifier = Modifier - .size(24.dp) - .noRippleClickable { - onShareClick() - }, - colorFilter = ColorFilter.tint(iconColor) // 根据背景透明度改变颜色 - ) - - Spacer(modifier = Modifier.width(16.dp)) - - // 右侧:菜单图标 - Image( - painter = painterResource(id = R.mipmap.menu_ico), + painter = painterResource(id = R.drawable.rider_pro_more_horizon), contentDescription = "菜单", modifier = Modifier .size(24.dp) From 82b991b91e399bd623f6c0a70ff8071250183d0a Mon Sep 17 00:00:00 2001 From: AllenTom Date: Thu, 13 Nov 2025 16:37:36 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=A7=AF=E5=88=86?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E5=88=97=E8=A1=A8=E6=BB=9A=E5=8A=A8=E7=A9=BF?= =?UTF-8?q?=E9=80=8F=E5=8F=8A=E4=BC=98=E5=8C=96=E5=8A=A0=E8=BD=BD=E6=9B=B4?= =?UTF-8?q?=E5=A4=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 通过`nestedScroll`修复`LazyColumn`在`ModalBottomSheet`中滚动时导致底部工作表(BottomSheet)也跟着滚动的问题。 - 移除手动点击的“加载更多”按钮,改为滚动到底部时自动加载更多积分记录。 - 引入`debounce`和`distinctUntilChanged`来优化滚动加载逻辑,防止重复触发。 - 在积分列表底部添加了“加载中”和“已经到底”的提示文本。 - 调整了Tab项的布局,使用`SpaceBetween`使其均匀分布。 --- .../ravenow/ui/points/PointsBottomSheet.kt | 155 ++++++++++++++++-- 1 file changed, 144 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/aiosman/ravenow/ui/points/PointsBottomSheet.kt b/app/src/main/java/com/aiosman/ravenow/ui/points/PointsBottomSheet.kt index 5661431..33902d7 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/points/PointsBottomSheet.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/points/PointsBottomSheet.kt @@ -13,7 +13,9 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.clickable @@ -30,8 +32,16 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.distinctUntilChanged import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.unit.Velocity import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource @@ -71,12 +81,17 @@ fun PointsBottomSheet( sheetState = sheetState, containerColor = AppColors.background ) { - Column( + Box( modifier = Modifier .fillMaxWidth() .fillMaxHeight(0.9f) - .padding(horizontal = 16.dp, vertical = 8.dp) ) { + Column( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight() + .padding(horizontal = 16.dp, vertical = 8.dp) + ) { // 头部 Row( modifier = Modifier.fillMaxWidth(), @@ -156,7 +171,7 @@ fun PointsBottomSheet( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp), - horizontalArrangement = Arrangement.Start, + horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { TabItem( @@ -165,7 +180,6 @@ fun PointsBottomSheet( onClick = { tab = 0 }, modifier = Modifier.weight(1f) ) - TabSpacer() TabItem( text = stringResource(R.string.how_to_earn), isSelected = tab == 1, @@ -186,6 +200,7 @@ fun PointsBottomSheet( } else { HowToEarnList() } + } } } } @@ -211,9 +226,75 @@ private fun PointsHistoryList( ) { val AppColors = LocalAppTheme.current val numberFormat = remember { NumberFormat.getNumberInstance(Locale.getDefault()) } + val listState = rememberLazyListState() + val loading = PointsViewModel.loading + var lastLoadTriggeredIndex by remember { mutableStateOf(-1) } + var previousItemsSize by remember { mutableStateOf(items.size) } + + // 创建 NestedScrollConnection 来阻止滚动事件向上传播到 ModalBottomSheet + // 使用 onPostScroll 来消费 LazyColumn 处理后的剩余滚动事件 + val nestedScrollConnection = remember { + object : NestedScrollConnection { + override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { + // 不消费任何事件,让 LazyColumn 先处理 + return Offset.Zero + } + + 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 + } + } + } + + // 当数据加载完成后,重置触发索引,以便可以继续加载 + LaunchedEffect(items.size, loading) { + if (items.size > previousItemsSize && !loading) { + // 数据已加载完成,重置触发索引 + lastLoadTriggeredIndex = -1 + previousItemsSize = items.size + } else if (items.size != previousItemsSize) { + previousItemsSize = items.size + } + } + + // 监听滚动位置,接近底部时自动加载更多 + LaunchedEffect(Unit) { + snapshotFlow { + listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: -1 + } + .debounce(500) // 防抖500ms,等待滚动停止后再触发,避免快速滚动时频繁触发 + .distinctUntilChanged() // 只在值变化时触发 + .collect { lastVisibleIndex -> + if (lastVisibleIndex >= 0 && hasNext && !loading) { + val totalItems = items.size + // 当滚动到倒数第3个item时,触发加载更多 + // 并且确保不会重复触发(检查上次触发的索引) + if (totalItems > 0 && + lastVisibleIndex >= totalItems - 3 && + lastVisibleIndex != lastLoadTriggeredIndex) { + lastLoadTriggeredIndex = lastVisibleIndex + onLoadMore() + } + } + } + } LazyColumn( - modifier = Modifier.fillMaxWidth() + state = listState, + modifier = Modifier + .fillMaxWidth() + .nestedScroll(nestedScrollConnection) ) { items(items) { item -> Row( @@ -249,12 +330,37 @@ private fun PointsHistoryList( ) } } - if (hasNext) { + // 显示加载状态 + if (loading && items.isNotEmpty()) { item { - Button(onClick = onLoadMore, modifier = Modifier - .fillMaxWidth() - .padding(top = 8.dp)) { - Text(stringResource(R.string.load_more)) + Box( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = stringResource(R.string.load_more), + color = AppColors.secondaryText, + fontSize = 14.sp + ) + } + } + } + // 显示已经到底提示 + if (!hasNext && items.isNotEmpty()) { + item { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = "已经到底", + color = AppColors.secondaryText, + fontSize = 14.sp + ) } } } @@ -265,6 +371,31 @@ private fun PointsHistoryList( private fun HowToEarnList() { val AppColors = LocalAppTheme.current + // 创建 NestedScrollConnection 来阻止滚动事件向上传播到 ModalBottomSheet + val nestedScrollConnection = remember { + object : NestedScrollConnection { + override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { + // 不消费任何事件,让 LazyColumn 先处理 + return Offset.Zero + } + + 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 + } + } + } + @Composable fun RowItem(title: String, desc: String, amount: String) { Row( @@ -294,7 +425,9 @@ private fun HowToEarnList() { } LazyColumn( - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() + .nestedScroll(nestedScrollConnection) ) { item { RowItem(stringResource(R.string.new_user_reward), stringResource(R.string.new_user_reward_desc), "+500")