我的-页面顶部导航栏ui修改,增加下滑时顶部导航栏的变化效果以及壁纸头像大小修正

This commit is contained in:
2025-11-07 14:36:25 +08:00
parent e714f567b9
commit 0bc442762d

View File

@@ -7,6 +7,7 @@ import android.util.Log
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -100,6 +101,13 @@ 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.text.style.TextAlign
import androidx.compose.ui.graphics.Brush
import java.text.NumberFormat
import java.util.Locale
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
@Composable
@@ -134,7 +142,8 @@ fun ProfileV3(
val context = LocalContext.current
val scope = rememberCoroutineScope()
val navController = LocalNavController.current
val bannerHeight = 400
val bannerWidth = 402
val bannerHeight = 206
val pickBannerImageLauncher = pickupAndCompressLauncher(
context,
scope,
@@ -156,12 +165,13 @@ fun ProfileV3(
val gridState = rememberLazyGridState()
val scrollState = rememberScrollState()
val toolbarAlpha by remember {
// 计算导航栏背景透明度根据滚动位置从0到1
val toolbarBackgroundAlpha by remember {
derivedStateOf {
if (!isSelf) {
1f
} else {
val maxScroll = 500f // 最大滚动距离,可调整
val maxScroll = 600f // 增加最大滚动距离,让渐变更平缓
val progress = (scrollState.value.coerceAtMost(maxScroll.toInt()) / maxScroll).coerceIn(0f, 1f)
progress
}
@@ -308,12 +318,19 @@ fun ProfileV3(
modifier = Modifier
.fillMaxWidth()
.height(bannerHeight.dp)
.background(AppColors.profileBackground)
.background(AppColors.profileBackground),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(bannerHeight.dp - 24.dp)
.width(bannerWidth.dp)
.height(bannerHeight.dp)
.clip(
RoundedCornerShape(
bottomStart = 32.dp,
bottomEnd = 32.dp
)
)
.let {
if (isSelf && isMain) {
it.noRippleClickable {
@@ -326,13 +343,6 @@ fun ProfileV3(
it
}
}
.shadow(
elevation = 6.dp,
shape = RoundedCornerShape(
bottomStart = 32.dp,
bottomEnd = 32.dp
),
)
) {
CustomAsyncImage(
LocalContext.current,
@@ -347,6 +357,9 @@ fun ProfileV3(
Spacer(modifier = Modifier.height(100.dp))
}
// 壁纸下方间距
Spacer(modifier = Modifier.height(16.dp))
// 用户信息
Box(
modifier = Modifier
@@ -478,6 +491,9 @@ fun ProfileV3(
}
}
}
// 底部间距,增加滚动距离
Spacer(modifier = Modifier.height(100.dp))
}
// 顶部导航栏
@@ -486,9 +502,13 @@ fun ProfileV3(
isSelf = isSelf,
profile = profile,
navController = navController,
alpha = toolbarAlpha,
backgroundAlpha = toolbarBackgroundAlpha,
interactionCount = moments.sumOf { it.likeCount }, // 计算总点赞数作为互动数据
onMenuClick = {
showOtherUserMenu = true
},
onShareClick = {
// TODO: 实现分享功能
}
)
@@ -569,30 +589,161 @@ fun TopNavigationBar(
isSelf: Boolean,
profile: AccountProfileEntity?,
navController: androidx.navigation.NavController,
alpha: Float,
onMenuClick: () -> Unit = {}
backgroundAlpha: Float,
interactionCount: Int = 0,
onMenuClick: () -> Unit = {},
onShareClick: () -> Unit = {}
) {
val appColors = LocalAppTheme.current
val numberFormat = remember { NumberFormat.getNumberInstance(Locale.getDefault()) }
// 根据背景透明度决定图标颜色透明度为1时变黑否则为白色
val iconColor = if (backgroundAlpha >= 1f) Color.Black else Color.White
val cardBorderColor = if (backgroundAlpha >= 1f) Color.Black else Color.White
Box(
modifier = Modifier
.fillMaxWidth()
.graphicsLayer { this.alpha = alpha }
) {
Column(
val statusBarPadding = WindowInsets.systemBars.asPaddingValues()
val statusBarHeight = statusBarPadding.calculateTopPadding()
val navigationBarHeight = 56.dp // 增加导航栏高度,包括图标和额外空间
// 导航栏背景层,包括状态栏区域,根据滚动位置逐渐变白
val totalHeight = statusBarHeight + navigationBarHeight
val density = LocalDensity.current
val totalHeightPx = with(density) { totalHeight.toPx() }
// 根据滚动位置计算基础颜色从深色平滑过渡到白色透明度从初始值逐渐减到0
val baseColor = remember(backgroundAlpha) {
val smoothProgress = backgroundAlpha.coerceIn(0f, 1f)
// 初始状态:半透明深色,让白色图标清晰可见
val initialDarkAlpha = 0.12f
// 使用平滑的插值函数,让整个过渡更自然
val easedProgress = smoothProgress * smoothProgress * (3f - 2f * smoothProgress) // smoothstep
// 颜色值:从黑色(0)平滑过渡到白色(1)
val colorValue = easedProgress
// 透明度:从初始值(0.12f)逐渐减少到0
// 当smoothProgress从0到1时alpha从initialDarkAlpha减少到0
val alpha = initialDarkAlpha * (1f - easedProgress)
Color(
red = colorValue,
green = colorValue,
blue = colorValue,
alpha = alpha.coerceIn(0f, initialDarkAlpha)
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.background(appColors.profileBackground)
) {
StatusBarSpacer()
.height(totalHeight) // 状态栏高度 + 导航栏高度
.align(Alignment.TopCenter)
.background(
brush = Brush.verticalGradient(
colors = listOf(
baseColor, // 顶部保持基础颜色
baseColor, // 中间保持基础颜色
baseColor.copy(alpha = baseColor.alpha * 0.5f), // 底部过渡,逐渐变透明
Color.Transparent // 最底部完全透明
),
startY = 0f,
endY = totalHeightPx
)
)
)
// 功能按钮区域,图标和文字根据背景透明度改变颜色
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
.noRippleClickable {
},
.padding(top = 16.dp, bottom = 16.dp, end = 16.dp) // 增加上下内边距
.align(Alignment.TopEnd)
.padding(top = statusBarHeight), // 从状态栏下方开始
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),
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 = numberFormat.format(interactionCount),
fontSize = 14.sp,
fontWeight = FontWeight.W500,
color = 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))
// 右侧:菜单图标
Image(
painter = painterResource(id = R.mipmap.menu_ico),
contentDescription = "菜单",
modifier = Modifier
.size(24.dp)
.noRippleClickable {
if (isSelf && isMain) {
IndexViewModel.openDrawer = true
} else {
onMenuClick()
}
},
colorFilter = ColorFilter.tint(iconColor) // 根据背景透明度改变颜色
)
}
// 如果不是主页面,显示返回按钮和用户信息
if (!isMain) {
val statusBarPadding = WindowInsets.systemBars.asPaddingValues()
Row(
modifier = Modifier
.align(Alignment.TopStart)
.padding(horizontal = 16.dp, vertical = 8.dp)
.padding(top = statusBarPadding.calculateTopPadding()),
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(id = R.drawable.rider_pro_back_icon),
contentDescription = "Back",
@@ -601,7 +752,7 @@ fun TopNavigationBar(
navController.navigateUp()
}
.size(24.dp),
colorFilter = ColorFilter.tint(appColors.text)
colorFilter = ColorFilter.tint(Color.White)
)
Spacer(modifier = Modifier.width(8.dp))
CustomAsyncImage(
@@ -618,55 +769,81 @@ fun TopNavigationBar(
text = profile?.nickName ?: "",
fontSize = 16.sp,
fontWeight = FontWeight.W600,
color = appColors.text
)
}
Spacer(modifier = Modifier.weight(1f))
if (isSelf && isMain) {
Box(
modifier = Modifier
.size(24.dp)
.padding(16.dp)
)
} else if (!isSelf) {
Box(
modifier = Modifier
.noRippleClickable {
onMenuClick()
}
.padding(16.dp)
) {
Icon(
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
contentDescription = "菜单",
tint = appColors.text,
modifier = Modifier.size(24.dp)
color = Color.White
)
}
}
}
Spacer(modifier = Modifier.height(8.dp))
}
if (isSelf && isMain) {
Box(
modifier = Modifier
.align(Alignment.TopEnd)
.padding(top = 32.dp, end = 16.dp)
.noRippleClickable {
IndexViewModel.openDrawer = true
}
) {
Box(
modifier = Modifier.padding(16.dp)
) {
Icon(
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
contentDescription = "",
tint = appColors.text
}
// 分享图标(向上箭头)
@Composable
fun ShareIcon(
color: Color,
modifier: Modifier = Modifier
) {
Canvas(modifier = modifier) {
val strokeWidth = 2.dp.toPx()
val centerX = size.width / 2
val centerY = size.height / 2
// 绘制向上的箭头
// 底部横线
drawLine(
color = color,
start = Offset(centerX - 9.dp.toPx(), centerY + 6.dp.toPx()),
end = Offset(centerX + 9.dp.toPx(), centerY + 6.dp.toPx()),
strokeWidth = strokeWidth
)
// 顶部横线
drawLine(
color = color,
start = Offset(centerX - 5.dp.toPx(), centerY - 6.5.dp.toPx()),
end = Offset(centerX + 5.dp.toPx(), centerY - 6.5.dp.toPx()),
strokeWidth = strokeWidth
)
// 中间竖线
drawLine(
color = color,
start = Offset(centerX, centerY - 3.dp.toPx()),
end = Offset(centerX, centerY + 6.dp.toPx()),
strokeWidth = strokeWidth
)
}
}
}
}
// 菜单图标(三条横线)
@Composable
fun MenuIcon(
color: Color,
modifier: Modifier = Modifier
) {
Canvas(modifier = modifier) {
val strokeWidth = 2.dp.toPx()
val centerX = size.width / 2
val centerY = size.height / 2
val lineLength = 16.dp.toPx()
val spacing = 6.dp.toPx()
// 绘制三条横线
drawLine(
color = color,
start = Offset(centerX - lineLength / 2, centerY - spacing),
end = Offset(centerX + lineLength / 2, centerY - spacing),
strokeWidth = strokeWidth
)
drawLine(
color = color,
start = Offset(centerX - lineLength / 2, centerY),
end = Offset(centerX + lineLength / 2, centerY),
strokeWidth = strokeWidth
)
drawLine(
color = color,
start = Offset(centerX - lineLength / 2, centerY + spacing),
end = Offset(centerX + lineLength / 2, centerY + spacing),
strokeWidth = strokeWidth
)
}
}