我的-编辑-ui调整

This commit is contained in:
2025-11-06 21:34:08 +08:00
parent 703beb8d43
commit e714f567b9

View File

@@ -4,19 +4,26 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
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.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Edit as EditIcon
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@@ -28,10 +35,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.aiosman.ravenow.AppState import com.aiosman.ravenow.AppState
import com.aiosman.ravenow.AppStore import com.aiosman.ravenow.AppStore
@@ -39,26 +50,26 @@ import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.R import com.aiosman.ravenow.R
import com.aiosman.ravenow.ui.NavigationRoute import com.aiosman.ravenow.ui.NavigationRoute
import com.aiosman.ravenow.ui.comment.NoticeScreenHeader
import com.aiosman.ravenow.ui.composables.CustomAsyncImage import com.aiosman.ravenow.ui.composables.CustomAsyncImage
import com.aiosman.ravenow.ui.composables.StatusBarMaskLayout import com.aiosman.ravenow.ui.composables.StatusBarMaskLayout
import com.aiosman.ravenow.ui.composables.StatusBarSpacer
import com.aiosman.ravenow.ui.composables.form.FormTextInput
import com.aiosman.ravenow.ui.composables.debouncedClickable import com.aiosman.ravenow.ui.composables.debouncedClickable
import com.aiosman.ravenow.ui.composables.rememberDebouncedNavigation import com.aiosman.ravenow.ui.composables.rememberDebouncedNavigation
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import android.util.Log import android.util.Log
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.res.painterResource import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import com.aiosman.ravenow.ConstVars import com.aiosman.ravenow.ConstVars
import com.aiosman.ravenow.ui.composables.pickupAndCompressLauncher import com.aiosman.ravenow.ui.composables.pickupAndCompressLauncher
import java.io.File import java.io.File
@@ -134,29 +145,308 @@ fun AccountEditScreen2(onUpdateBanner: ((Uri, File, Context) -> Unit)? = null,)
// 确保显示的是当前登录用户的信息,而不是之前用户的缓存数据 // 确保显示的是当前登录用户的信息,而不是之前用户的缓存数据
model.reloadProfile() model.reloadProfile()
} }
// 设置状态栏为透明,使用浅色图标(因为顶部背景是深色图片)
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Transparent, darkIcons = false)
}
StatusBarMaskLayout( StatusBarMaskLayout(
modifier = Modifier.background(color = appColors.background).padding(horizontal = 16.dp), modifier = Modifier.background(Color(0xFFFAF9FB)),
darkIcons = !AppState.darkMode, darkIcons = false, // 浅色图标(白色),因为顶部背景是深色
maskBoxBackgroundColor = appColors.background maskBoxBackgroundColor = Color.Transparent
) { ) {
Column( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(color = appColors.background), .background(Color(0xFFFAF9FB))
horizontalAlignment = Alignment.CenterHorizontally
) { ) {
//StatusBarSpacer() when {
model.isLoading -> {
// 加载中状态
Box( Box(
modifier = Modifier.padding(horizontal = 0.dp, vertical = 16.dp) modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) { ) {
NoticeScreenHeader( androidx.compose.material3.CircularProgressIndicator(
title = stringResource(R.string.edit_profile), color = appColors.main
moreIcon = false )
}
}
model.profile != null -> {
Column(
modifier = Modifier.fillMaxSize()
) { ) {
// 顶部背景区域(圆角在底部,覆盖状态栏)
Icon( val banner = model.profile?.banner
val statusBarPadding = WindowInsets.systemBars.asPaddingValues().calculateTopPadding()
Box(
modifier = Modifier modifier = Modifier
.size(24.dp) .fillMaxWidth()
.offset(y = -statusBarPadding)
) {
Box(
modifier = Modifier
.width(402.dp)
.height(206.dp)
.align(Alignment.TopCenter)
.clip(RoundedCornerShape(bottomStart = 32.dp, bottomEnd = 32.dp))
) {
if (banner != null) {
CustomAsyncImage(
context = context,
imageUrl = banner,
modifier = Modifier.fillMaxSize(),
contentDescription = "Banner",
contentScale = ContentScale.Crop
)
} else {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Gray.copy(alpha = 0.2f))
)
}
// 更换封面按钮(位于壁纸右下方)
Box(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(end = 16.dp, bottom = 20.dp)
.clip(RoundedCornerShape(12.dp))
.background(Color(0x5C7D7C80)) // RGB(125, 120, 128, 0.36)
.padding(horizontal = 8.dp, vertical = 4.dp)
.noRippleClickable {
Intent(Intent.ACTION_PICK).apply {
type = "image/*"
pickBannerImageLauncher.launch(this)
}
}
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
// 更换封面图标
Icon(
painter = painterResource(
id = if (AppState.darkMode) {
// TODO: 添加更换封面暗色模式图标
R.mipmap.frame_4 // 临时占位,需替换为实际图标
} else {
// TODO: 添加更换封面亮色模式图标
R.mipmap.fengm // 临时占位,需替换为实际图标
}
),
contentDescription = null,
modifier = Modifier.size(16.dp),
tint = Color.White
)
Text(
text = "更换封面",
fontSize = 12.sp,
color = Color.White
)
}
}
// 状态栏区域(时间、信号、电池)
// 这里使用系统状态栏,不单独实现
// 导航栏区域
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
Spacer(modifier = Modifier.height(statusBarPadding + 12.dp))
Box(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 12.dp)
) {
// 返回按钮
Box(
modifier = Modifier
.size(44.dp)
.clip(CircleShape)
.background(Color.White.copy(alpha = 0.3f))
.noRippleClickable {
navController.navigateUp()
}
.align(Alignment.CenterStart),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(id = R.drawable.rider_pro_back_icon),
contentDescription = "Back",
modifier = Modifier.size(24.dp),
colorFilter = ColorFilter.tint(Color.White)
)
}
// 标题
Text(
text = "编辑资料",
fontSize = 17.sp,
fontWeight = FontWeight.SemiBold,
color = Color.White,
modifier = Modifier.align(Alignment.Center)
)
}
}
}
}
// 用户头像区域(距离顶部-50dp包含状态栏高度左右居中
Box(
modifier = Modifier
.fillMaxWidth()
.offset(y = (-50).dp - statusBarPadding),
contentAlignment = Alignment.Center
) {
val it = model.profile!!
Box(
modifier = Modifier.size(96.dp),
contentAlignment = Alignment.BottomEnd
) {
// 头像
CustomAsyncImage(
context,
model.croppedBitmap ?: it.avatar,
modifier = Modifier
.size(96.dp)
.clip(CircleShape)
.border(2.4.dp, Color(0xFFFAF9FB), CircleShape),
contentDescription = "",
contentScale = ContentScale.Crop
)
// 编辑头像按钮
Box(
modifier = Modifier
.size(28.dp)
.clip(CircleShape)
.background(Color(0xFF110C13))
.border(2.dp, Color.White, CircleShape)
.debouncedClickable(debounceTime = 800L) {
debouncedNavigation {
navController.navigate(NavigationRoute.ImageCrop.route)
}
},
contentAlignment = Alignment.Center
) {
Icon(
painter = painterResource(
id = if (AppState.darkMode) {
// TODO: 添加编辑头像暗色模式图标
R.mipmap.frame_4 // 临时占位,需替换为实际图标
} else {
// TODO: 添加编辑头像亮色模式图标
R.mipmap.bi // 临时占位,需替换为实际图标
}
),
contentDescription = "Edit Avatar",
modifier = Modifier.size(16.dp),
tint = Color.White
)
}
}
}
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(horizontal = 16.dp)
.offset(y = (-74).dp)//
) {
// 昵称输入框
ProfileInfoCard(
label = "昵称",
value = model.name,
placeholder = "Value",
onValueChange = { onNicknameChange(it) },
isMultiline = false
)
Spacer(modifier = Modifier.height(16.dp))
// 个人简介输入框
ProfileInfoCard(
label = "个人简介",
value = model.bio,
placeholder = "Welcome to my fantiac word i will show you something about magic",
onValueChange = { onBioChange(it) },
isMultiline = true
)
Spacer(modifier = Modifier.height(16.dp))
// MBTI 类型和星座
Column(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(16.dp))
.background(Color.White)
) {
// MBTI 类型
ProfileSelectItem(
label = "MBTI 类型",
value = model.mbti ?: "ENFP",
iconColor = Color(0xFF7C45ED),
iconResDark = null, // TODO: 添加MBTI暗色模式图标
iconResLight = null, // TODO: 添加MBTI亮色模式图标
onClick = {
debouncedNavigation {
navController.navigate(NavigationRoute.MbtiSelect.route)
}
}
)
// 分隔线
Box(
modifier = Modifier
.fillMaxWidth()
.height(0.3.dp)
.background(Color(0x41413C43).copy(alpha = 0.2f))
.padding(horizontal = 16.dp)
)
// 星座(使用当前图标)
ProfileSelectItem(
label = "星座",
value = model.zodiac ?: "白羊座",
iconColor = Color(0xFFFFCC00),
iconResDark = R.mipmap.frame_4, // 星座暗色模式图标
iconResLight = R.mipmap.xingzuo, // 星座亮色模式图标
onClick = {
debouncedNavigation {
navController.navigate(NavigationRoute.ZodiacSelect.route)
}
}
)
}
Spacer(modifier = Modifier.weight(1f))
// 保存按钮
Box(
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.clip(RoundedCornerShape(1000.dp))
.background(
brush = Brush.horizontalGradient(
colors = listOf(
Color(0xFF7C45ED), // RGB(124, 69, 237) - 左侧
Color(0xFF7C57EE), // RGB(124, 87, 238) - 中间
Color(0xFF7BD8F8) // RGB(123, 216, 248) - 右侧
)
)
)
.debouncedClickable( .debouncedClickable(
enabled = validate() && !model.isUpdating, enabled = validate() && !model.isUpdating,
debounceTime = 1000L debounceTime = 1000L
@@ -174,177 +464,170 @@ fun AccountEditScreen2(onUpdateBanner: ((Uri, File, Context) -> Unit)? = null,)
} }
} }
}, },
imageVector = Icons.Default.Check, contentAlignment = Alignment.Center
contentDescription = "保存",
tint = if (validate() && !model.isUpdating) appColors.text else appColors.nonActiveText
)
}
}
// 添加横幅图片区域
val banner = model.profile?.banner
Box(
modifier = Modifier
.fillMaxWidth()
.height(300.dp)
.clip(RoundedCornerShape(12.dp))
) { ) {
if (banner != null) {
CustomAsyncImage(
context = LocalContext.current,
imageUrl = banner,
modifier = Modifier.fillMaxSize(),
contentDescription = "Banner",
contentScale = ContentScale.Crop
)
} else {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Gray.copy(alpha = 0.1f))
)
}
Box(
modifier = Modifier
.width(120.dp)
.height(42.dp)
.align(Alignment.BottomEnd)
.padding(end = 12.dp, bottom = 12.dp)
.background(
color = Color.Black.copy(alpha = 0.4f),
shape = RoundedCornerShape(9.dp)
)
.noRippleClickable {
Intent(Intent.ACTION_PICK).apply {
type = "image/*"
pickBannerImageLauncher.launch(this)
}
}
){
Text( Text(
text = "change", text = "保存",
fontSize = 14.sp, fontSize = 17.sp,
fontWeight = FontWeight.W600, fontWeight = FontWeight.Normal,
color = Color.White, color = Color.White
modifier = Modifier.align(Alignment.Center)
) )
} }
} }
Spacer(modifier = Modifier.height(20.dp))
// 显示内容或加载状态
Log.d("AccountEditScreen2", "UI状态 - profile: ${model.profile?.nickName}, isLoading: ${model.isLoading}")
when {
model.profile != null -> {
Log.d("AccountEditScreen2", "显示用户资料内容")
// 有数据时显示内容
val it = model.profile!!
Box(
modifier = Modifier.size(88.dp),
contentAlignment = Alignment.Center
) {
CustomAsyncImage(
context,
model.croppedBitmap ?: it.avatar,
modifier = Modifier
.size(88.dp)
.clip(
RoundedCornerShape(88.dp)
),
contentDescription = "",
contentScale = ContentScale.Crop
)
Box(
modifier = Modifier
.size(32.dp)
.clip(CircleShape)
.background(
brush = Brush.linearGradient(
colors = listOf(
Color(0xFF7c45ed),
Color(0x997c68ef),
Color(0xFF7bd8f8)
)
),
)
.align(Alignment.BottomEnd)
.debouncedClickable(
debounceTime = 800L
) {
debouncedNavigation {
navController.navigate(NavigationRoute.ImageCrop.route)
}
},
contentAlignment = Alignment.Center
) {
Icon(
Icons.Default.Add,
contentDescription = "Add",
tint = Color.White,
)
}
}
Spacer(modifier = Modifier.height(18.dp))
Column(
modifier = Modifier
.weight(1f)
.padding(horizontal = 16.dp)
) {
FormTextInput(
value = model.name,
label = stringResource(R.string.nickname),
hint = "Input nickname",
modifier = Modifier.fillMaxWidth(),
error = usernameError
) { value ->
onNicknameChange(value)
}
FormTextInput(
value = model.bio,
label = stringResource(R.string.bio),
hint = "Input bio",
modifier = Modifier.fillMaxWidth(),
error = bioError
) { value ->
onBioChange(value)
}
}
}
model.isLoading -> {
Log.d("AccountEditScreen2", "显示加载指示器")
// 加载中状态
Box(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
contentAlignment = Alignment.Center
) {
androidx.compose.material3.CircularProgressIndicator(
color = appColors.main
)
} }
} }
else -> { else -> {
Log.d("AccountEditScreen2", "显示错误信息 - 没有数据且不在加载中")
// 没有数据且不在加载中,显示错误信息 // 没有数据且不在加载中,显示错误信息
Box( Box(
modifier = Modifier modifier = Modifier.fillMaxSize(),
.fillMaxSize()
.padding(16.dp),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
androidx.compose.material3.Text( Text(
text = "加载用户资料失败,请重试", text = "加载用户资料失败,请重试",
color = appColors.text color = appColors.text
) )
} }
} }
} }
}
}} }
}
/**
* 信息输入卡片组件
*/
@Composable
fun ProfileInfoCard(
label: String,
value: String,
placeholder: String,
onValueChange: (String) -> Unit,
isMultiline: Boolean = false
) {
val appColors = LocalAppTheme.current
Box(
modifier = Modifier
.fillMaxWidth()
.height(if (isMultiline) 66.dp else 56.dp) // 昵称框高度56dp个人简介66dp
.clip(RoundedCornerShape(16.dp))
.background(Color.White),
contentAlignment = if (isMultiline) Alignment.TopStart else Alignment.CenterStart
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(vertical = if (isMultiline) 11.dp else 0.dp),
verticalAlignment = if (isMultiline) Alignment.Top else Alignment.CenterVertically
) {
// 标签
Text(
text = label,
fontSize = 17.sp,
fontWeight = FontWeight.Normal,
color = Color.Black,
modifier = Modifier.width(100.dp)
)
Spacer(modifier = Modifier.width(16.dp))
// 输入框
Box(
modifier = Modifier.weight(1f)
) {
if (value.isEmpty()) {
Text(
text = placeholder,
fontSize = if (isMultiline) 15.sp else 17.sp,
fontWeight = FontWeight.Normal,
color = Color(0x993C3C43),
modifier = Modifier.fillMaxWidth()
)
}
BasicTextField(
value = value,
onValueChange = onValueChange,
modifier = Modifier.fillMaxWidth(),
textStyle = androidx.compose.ui.text.TextStyle(
fontSize = if (isMultiline) 15.sp else 17.sp,
fontWeight = FontWeight.Normal,
color = Color.Black
),
cursorBrush = SolidColor(Color.Black),
maxLines = if (isMultiline) Int.MAX_VALUE else 1,
singleLine = !isMultiline
)
}
}
}
}
/**
* 选择项组件MBTI、星座
*/
@Composable
fun ProfileSelectItem(
label: String,
value: String,
iconColor: Color,
onClick: () -> Unit,
iconResDark: Int? = null,
iconResLight: Int? = null
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.clickable(onClick = onClick)
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
// 自定义图标
Icon(
painter = painterResource(
id = if (AppState.darkMode) {
iconResDark ?: R.mipmap.frame_4 // 使用传入的暗色模式图标,或默认占位
} else {
iconResLight ?: R.mipmap.naoz // 使用传入的亮色模式图标,或默认占位
}
),
contentDescription = null,
modifier = Modifier.size(24.dp),
tint = iconColor
)
Text(
text = label,
fontSize = 17.sp,
fontWeight = FontWeight.Normal,
color = Color.Black
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
text = value,
fontSize = 17.sp,
fontWeight = FontWeight.Normal,
color = Color(0x993C3C43)
)
Icon(
imageVector = Icons.Default.ArrowForward,
contentDescription = null,
modifier = Modifier.size(8.dp),
tint = Color(0x4D3C3C43)
)
}
}
} }