注册账号界面ui调整

This commit is contained in:
2025-11-07 21:25:02 +08:00
parent f86b5e1d39
commit c100a8ceef
2 changed files with 177 additions and 97 deletions

View File

@@ -43,6 +43,10 @@ import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.R
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
private val LabelTextColor = Color(red = 60f / 255f, green = 60f / 255f, blue = 67f / 255f, alpha = 0.6f)
private val HintTextColor = Color(red = 60f / 255f, green = 60f / 255f, blue = 67f / 255f, alpha = 0.3f)
private val PasswordIconColor = Color(red = 17f / 255f, green = 12f / 255f, blue = 19f / 255f)
@Composable
fun TextInputField(
modifier: Modifier = Modifier,
@@ -52,69 +56,96 @@ fun TextInputField(
label: String? = null,
hint: String? = null,
error: String? = null,
enabled: Boolean = true
enabled: Boolean = true,
leadingIcon: @Composable (() -> Unit)? = null,
customBackgroundColor: Color? = null,
customCornerRadius: Float = 24f
) {
val AppColors = LocalAppTheme.current
var showPassword by remember { mutableStateOf(!password) }
var isFocused by remember { mutableStateOf(false) }
val backgroundColor = customBackgroundColor ?: AppColors.inputBackground
Column(modifier = modifier) {
label?.let {
Text(it, color = AppColors.secondaryText)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = it,
color = LabelTextColor,
fontSize = 13.sp,
modifier = Modifier.padding(start = 8.dp, top = 8.dp, bottom = 8.dp)
)
}
Box(
contentAlignment = Alignment.CenterStart,
modifier = Modifier
.clip(RoundedCornerShape(24.dp))
.background(AppColors.inputBackground)
.clip(RoundedCornerShape(customCornerRadius.dp))
.background(backgroundColor)
.border(
width = 2.dp,
color = if (error == null) Color.Transparent else AppColors.error,
shape = RoundedCornerShape(24.dp)
shape = RoundedCornerShape(customCornerRadius.dp)
)
.padding(horizontal = 16.dp, vertical = 16.dp)
) {
Row(verticalAlignment = Alignment.CenterVertically){
BasicTextField(
value = text,
onValueChange = onValueChange,
modifier = Modifier
.weight(1f)
.onFocusChanged { focusState ->
isFocused = focusState.isFocused
},
textStyle = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.W500,
color = AppColors.text
),
keyboardOptions = KeyboardOptions(
keyboardType = if (password) KeyboardType.Password else KeyboardType.Text
),
visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(),
singleLine = true,
enabled = enabled,
cursorBrush = SolidColor(AppColors.text),
)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
){
leadingIcon?.let {
Box(modifier = Modifier.size(24.dp)) {
it()
}
Spacer(modifier = Modifier.size(12.dp))
}
Box(modifier = Modifier.weight(1f)) {
BasicTextField(
value = text,
onValueChange = onValueChange,
modifier = Modifier
.fillMaxWidth()
.onFocusChanged { focusState ->
isFocused = focusState.isFocused
},
textStyle = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.W400,
color = AppColors.text
),
keyboardOptions = KeyboardOptions(
keyboardType = if (password) KeyboardType.Password else KeyboardType.Email
),
visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(),
singleLine = true,
enabled = enabled,
cursorBrush = SolidColor(AppColors.text),
)
if (text.isEmpty() && hint != null) {
Text(
text = hint,
color = HintTextColor,
fontSize = 16.sp,
fontWeight = FontWeight.W400
)
}
}
if (password) {
Image(
painter = painterResource(id = R.drawable.rider_pro_eye),
painter = painterResource(
id = if (showPassword) {
R.drawable.rider_pro_eye
} else {
R.mipmap.icon_eyes_closed_light
}
),
contentDescription = "Password",
modifier = Modifier
.size(18.dp)
.size(24.dp)
.noRippleClickable {
showPassword = !showPassword
},
colorFilter = ColorFilter.tint(AppColors.text)
colorFilter = ColorFilter.tint(PasswordIconColor)
)
}
}
if (text.isEmpty()) {
hint?.let {
Text(it, modifier = Modifier.padding(start = 5.dp), color = AppColors.inputHint, fontWeight = FontWeight.W600)
}
}
}
Spacer(modifier = Modifier.height(8.dp))
Row(

View File

@@ -1,15 +1,19 @@
package com.aiosman.ravenow.ui.login
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -19,9 +23,13 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.aiosman.ravenow.AppState
import com.aiosman.ravenow.AppStore
import com.aiosman.ravenow.LocalAppTheme
@@ -33,25 +41,28 @@ import com.aiosman.ravenow.data.ServiceException
import com.aiosman.ravenow.data.AccountServiceImpl
import com.aiosman.ravenow.data.api.getErrorMessageCode
import com.aiosman.ravenow.ui.NavigationRoute
import com.aiosman.ravenow.ui.comment.NoticeScreenHeader
import com.aiosman.ravenow.ui.composables.ActionButton
import com.aiosman.ravenow.ui.composables.CheckboxWithLabel
import com.aiosman.ravenow.ui.composables.PolicyCheckbox
import com.aiosman.ravenow.ui.composables.StatusBarSpacer
import com.aiosman.ravenow.ui.composables.TextInputField
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import com.aiosman.ravenow.utils.PasswordValidator
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
private val LightGrayBackground = Color(red = 250f / 255f, green = 249f / 255f, blue = 251f / 255f)
private val IconGray = Color(red = 151f / 255f, green = 148f / 255f, blue = 153f / 255f)
private val PurpleButton = Color(0xFF7C45ED)
@Composable
fun EmailSignupScreen() {
var appColor = LocalAppTheme.current
val appColor = LocalAppTheme.current
var email by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var confirmPassword by remember { mutableStateOf("") }
var rememberMe by remember { mutableStateOf(false) }
var acceptTerms by remember { mutableStateOf(false) }
var acceptPromotions by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
val navController = LocalNavController.current
val context = LocalContext.current
@@ -60,7 +71,6 @@ fun EmailSignupScreen() {
var passwordError by remember { mutableStateOf<String?>(null) }
var confirmPasswordError by remember { mutableStateOf<String?>(null) }
var termsError by remember { mutableStateOf<Boolean>(false) }
var promotionsError by remember { mutableStateOf<Boolean>(false) }
fun validateForm(): Boolean {
emailError = when {
// 非空
@@ -88,22 +98,8 @@ fun EmailSignupScreen() {
}
termsError = true
return false
} else {
termsError = false
}
if (!acceptPromotions) {
scope.launch(Dispatchers.Main) {
Toast.makeText(
context,
context.getString(R.string.error_not_accept_recive_notice),
Toast.LENGTH_SHORT
).show()
}
promotionsError = true
return false
} else {
promotionsError = false
}
termsError = false
return emailError == null && passwordError == null && confirmPasswordError == null
}
@@ -158,63 +154,127 @@ fun EmailSignupScreen() {
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
.background(appColor.background)
) {
StatusBarSpacer()
Box(
// 顶部导航栏:返回箭头 + "注册"标题,左对齐
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp, start = 16.dp, end = 16.dp)
.padding(top = 15.dp, start = 16.dp, bottom = 15.dp, end = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
NoticeScreenHeader(stringResource(R.string.sign_up_upper), moreIcon = false)
Image(
painter = painterResource(id = R.drawable.rider_pro_back_icon),
contentDescription = "Back",
modifier = Modifier
.size(24.dp)
.noRippleClickable {
navController.navigateUp()
},
colorFilter = ColorFilter.tint(Color.Black)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = stringResource(R.string.sign_up_upper),
fontSize = 20.sp,
fontWeight = FontWeight.W600,
color = Color.Black
)
}
Spacer(modifier = Modifier.padding(32.dp))
// 输入区域
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(horizontal = 24.dp)
.padding(horizontal = 0.dp)
) {
Spacer(modifier = Modifier.height(16.dp))
// 邮箱输入框
TextInputField(
modifier = Modifier
.fillMaxWidth(),
.fillMaxWidth()
.padding(horizontal = 24.dp),
text = email,
onValueChange = {
email = it
},
hint = stringResource(R.string.text_hint_email),
error = emailError
label = stringResource(R.string.login_email_label),
hint = "输入电子邮件",
error = emailError,
leadingIcon = {
Image(
painter = painterResource(id = R.mipmap.icon_email_light),
contentDescription = "Email",
modifier = Modifier.size(24.dp),
colorFilter = ColorFilter.tint(IconGray)
)
},
customBackgroundColor = LightGrayBackground,
customCornerRadius = 16f
)
Spacer(modifier = Modifier.padding(4.dp))
// 密码输入框
TextInputField(
modifier = Modifier
.fillMaxWidth(),
.fillMaxWidth()
.padding(horizontal = 24.dp),
text = password,
onValueChange = {
password = it
},
password = true,
label = stringResource(R.string.text_hint_password).replace("输入", ""),
hint = stringResource(R.string.text_hint_password),
error = passwordError
error = passwordError,
leadingIcon = {
Image(
painter = painterResource(id = R.mipmap.icon_lock_light),
contentDescription = "Lock",
modifier = Modifier.size(24.dp),
colorFilter = ColorFilter.tint(IconGray)
)
},
customBackgroundColor = LightGrayBackground,
customCornerRadius = 16f
)
Spacer(modifier = Modifier.padding(4.dp))
// 确认密码输入框
TextInputField(
modifier = Modifier
.fillMaxWidth(),
.fillMaxWidth()
.padding(horizontal = 24.dp),
text = confirmPassword,
onValueChange = {
confirmPassword = it
},
password = true,
hint = stringResource(R.string.text_hint_confirm_password),
error = confirmPasswordError
label = stringResource(R.string.confirm_password_label),
hint = stringResource(R.string.text_hint_password),
error = confirmPasswordError,
leadingIcon = {
Image(
painter = painterResource(id = R.mipmap.icon_lock_light),
contentDescription = "Lock",
modifier = Modifier.size(24.dp),
colorFilter = ColorFilter.tint(IconGray)
)
},
customBackgroundColor = LightGrayBackground,
customCornerRadius = 16f
)
Spacer(modifier = Modifier.height(8.dp))
Spacer(modifier = Modifier.height(16.dp))
// 功能选项区域
Column(
modifier = Modifier.fillMaxWidth(),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp),
horizontalAlignment = Alignment.Start,
) {
CheckboxWithLabel(
@@ -236,42 +296,31 @@ fun EmailSignupScreen() {
termsError = false
}
}
Spacer(modifier = Modifier.height(16.dp))
CheckboxWithLabel(
checked = acceptPromotions,
checkSize = 16,
fontSize = 12,
label = stringResource(R.string.agree_promotion),
error = promotionsError
) {
acceptPromotions = it
// 当用户勾选时,立即清除错误状态
if (it) {
promotionsError = false
}
}
}
Spacer(modifier = Modifier.height(32.dp))
Spacer(modifier = Modifier.height(76.dp))
// 底部注册按钮
Box(
modifier = Modifier.fillMaxWidth(),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp),
contentAlignment = Alignment.Center
) {
ActionButton(
modifier = Modifier
.width(345.dp),
text = stringResource(R.string.lets_ride_upper),
backgroundColor = Color(0xffda3832),
color = Color.White
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.sign_up_upper),
backgroundColor = PurpleButton,
color = Color.White,
fontSize = 17.sp,
fontWeight = FontWeight.W600
) {
scope.launch(Dispatchers.IO) {
registerUser()
}
}
}
}
}
}