我的-页面顶部导航栏ui修改,增加下滑时顶部导航栏的变化效果以及壁纸头像大小修正
This commit is contained in:
@@ -7,6 +7,7 @@ import android.util.Log
|
|||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -100,6 +101,13 @@ import kotlinx.coroutines.launch
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.ui.res.stringResource
|
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)
|
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -134,7 +142,8 @@ fun ProfileV3(
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
val bannerHeight = 400
|
val bannerWidth = 402
|
||||||
|
val bannerHeight = 206
|
||||||
val pickBannerImageLauncher = pickupAndCompressLauncher(
|
val pickBannerImageLauncher = pickupAndCompressLauncher(
|
||||||
context,
|
context,
|
||||||
scope,
|
scope,
|
||||||
@@ -156,12 +165,13 @@ fun ProfileV3(
|
|||||||
val gridState = rememberLazyGridState()
|
val gridState = rememberLazyGridState()
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
|
|
||||||
val toolbarAlpha by remember {
|
// 计算导航栏背景透明度,根据滚动位置从0到1
|
||||||
|
val toolbarBackgroundAlpha by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
if (!isSelf) {
|
if (!isSelf) {
|
||||||
1f
|
1f
|
||||||
} else {
|
} else {
|
||||||
val maxScroll = 500f // 最大滚动距离,可调整
|
val maxScroll = 600f // 增加最大滚动距离,让渐变更平缓
|
||||||
val progress = (scrollState.value.coerceAtMost(maxScroll.toInt()) / maxScroll).coerceIn(0f, 1f)
|
val progress = (scrollState.value.coerceAtMost(maxScroll.toInt()) / maxScroll).coerceIn(0f, 1f)
|
||||||
progress
|
progress
|
||||||
}
|
}
|
||||||
@@ -308,12 +318,19 @@ fun ProfileV3(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(bannerHeight.dp)
|
.height(bannerHeight.dp)
|
||||||
.background(AppColors.profileBackground)
|
.background(AppColors.profileBackground),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.width(bannerWidth.dp)
|
||||||
.height(bannerHeight.dp - 24.dp)
|
.height(bannerHeight.dp)
|
||||||
|
.clip(
|
||||||
|
RoundedCornerShape(
|
||||||
|
bottomStart = 32.dp,
|
||||||
|
bottomEnd = 32.dp
|
||||||
|
)
|
||||||
|
)
|
||||||
.let {
|
.let {
|
||||||
if (isSelf && isMain) {
|
if (isSelf && isMain) {
|
||||||
it.noRippleClickable {
|
it.noRippleClickable {
|
||||||
@@ -326,13 +343,6 @@ fun ProfileV3(
|
|||||||
it
|
it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.shadow(
|
|
||||||
elevation = 6.dp,
|
|
||||||
shape = RoundedCornerShape(
|
|
||||||
bottomStart = 32.dp,
|
|
||||||
bottomEnd = 32.dp
|
|
||||||
),
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
CustomAsyncImage(
|
CustomAsyncImage(
|
||||||
LocalContext.current,
|
LocalContext.current,
|
||||||
@@ -347,6 +357,9 @@ fun ProfileV3(
|
|||||||
Spacer(modifier = Modifier.height(100.dp))
|
Spacer(modifier = Modifier.height(100.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 壁纸下方间距
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
// 用户信息
|
// 用户信息
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -478,6 +491,9 @@ fun ProfileV3(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 底部间距,增加滚动距离
|
||||||
|
Spacer(modifier = Modifier.height(100.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 顶部导航栏
|
// 顶部导航栏
|
||||||
@@ -486,9 +502,13 @@ fun ProfileV3(
|
|||||||
isSelf = isSelf,
|
isSelf = isSelf,
|
||||||
profile = profile,
|
profile = profile,
|
||||||
navController = navController,
|
navController = navController,
|
||||||
alpha = toolbarAlpha,
|
backgroundAlpha = toolbarBackgroundAlpha,
|
||||||
|
interactionCount = moments.sumOf { it.likeCount }, // 计算总点赞数作为互动数据
|
||||||
onMenuClick = {
|
onMenuClick = {
|
||||||
showOtherUserMenu = true
|
showOtherUserMenu = true
|
||||||
|
},
|
||||||
|
onShareClick = {
|
||||||
|
// TODO: 实现分享功能
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -569,107 +589,264 @@ fun TopNavigationBar(
|
|||||||
isSelf: Boolean,
|
isSelf: Boolean,
|
||||||
profile: AccountProfileEntity?,
|
profile: AccountProfileEntity?,
|
||||||
navController: androidx.navigation.NavController,
|
navController: androidx.navigation.NavController,
|
||||||
alpha: Float,
|
backgroundAlpha: Float,
|
||||||
onMenuClick: () -> Unit = {}
|
interactionCount: Int = 0,
|
||||||
|
onMenuClick: () -> Unit = {},
|
||||||
|
onShareClick: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
val appColors = LocalAppTheme.current
|
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(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.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
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.background(appColors.profileBackground)
|
.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(top = 16.dp, bottom = 16.dp, end = 16.dp) // 增加上下内边距
|
||||||
|
.align(Alignment.TopEnd)
|
||||||
|
.padding(top = statusBarHeight), // 从状态栏下方开始
|
||||||
|
horizontalArrangement = Arrangement.End,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
StatusBarSpacer()
|
// 左侧:互动数据卡片
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.height(24.dp)
|
||||||
.padding(horizontal = 16.dp, vertical = 8.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 {
|
.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
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
if (!isMain) {
|
Image(
|
||||||
Image(
|
painter = painterResource(id = R.drawable.rider_pro_back_icon),
|
||||||
painter = painterResource(id = R.drawable.rider_pro_back_icon),
|
contentDescription = "Back",
|
||||||
contentDescription = "Back",
|
modifier = Modifier
|
||||||
modifier = Modifier
|
.noRippleClickable {
|
||||||
.noRippleClickable {
|
navController.navigateUp()
|
||||||
navController.navigateUp()
|
}
|
||||||
}
|
.size(24.dp),
|
||||||
.size(24.dp),
|
colorFilter = ColorFilter.tint(Color.White)
|
||||||
colorFilter = ColorFilter.tint(appColors.text)
|
)
|
||||||
)
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
CustomAsyncImage(
|
||||||
CustomAsyncImage(
|
LocalContext.current,
|
||||||
LocalContext.current,
|
profile?.avatar,
|
||||||
profile?.avatar,
|
modifier = Modifier
|
||||||
modifier = Modifier
|
.size(32.dp)
|
||||||
.size(32.dp)
|
.clip(CircleShape),
|
||||||
.clip(CircleShape),
|
contentDescription = "",
|
||||||
contentDescription = "",
|
contentScale = ContentScale.Crop
|
||||||
contentScale = ContentScale.Crop
|
)
|
||||||
)
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
Spacer(modifier = Modifier.width(16.dp))
|
Text(
|
||||||
Text(
|
text = profile?.nickName ?: "",
|
||||||
text = profile?.nickName ?: "",
|
fontSize = 16.sp,
|
||||||
fontSize = 16.sp,
|
fontWeight = FontWeight.W600,
|
||||||
fontWeight = FontWeight.W600,
|
color = Color.White
|
||||||
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)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Agent菜单弹窗
|
* Agent菜单弹窗
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user