更新 UI

This commit is contained in:
2024-07-19 10:05:06 +08:00
parent e2e58ea955
commit baee6f66dc
16 changed files with 414 additions and 65 deletions

View File

@@ -2,6 +2,7 @@ package com.aiosman.riderpro
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -13,9 +14,16 @@ 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.items
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -23,11 +31,16 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PathEffect
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -35,6 +48,7 @@ import androidx.compose.ui.unit.sp
@Preview
@Composable
fun EditModification() {
val model = NewPostViewModel
Column(
modifier = Modifier
.fillMaxSize()
@@ -44,28 +58,62 @@ fun EditModification() {
modifier = Modifier.padding(vertical = 16.dp, horizontal = 18.dp)
) {
NoticeScreenHeader("Modification List")
}
LazyColumn(
modifier = Modifier.padding(start = 24.dp, end = 24.dp, top = 16.dp)
) {
item {
repeat(2) {
AddModificationItem()
Spacer(modifier = Modifier.height(16.dp))
items(model.modificationList) { mod ->
AddModificationItem(mod) { updatedMod ->
model.modificationList = model.modificationList.map { existingMod ->
if (existingMod.key == updatedMod.key) updatedMod else existingMod
}.toMutableList()
}
Spacer(modifier = Modifier.height(16.dp))
}
item {
AddModificationButton {
model.modificationList += Modification(
key = Utils.generateRandomString(4),
name = "",
price = "0.0"
)
}
AddModificationButton()
}
}
}
}
data class Modification(
val key: String,
val name: String,
val price: String
)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AddModificationItem() {
var text by remember { mutableStateOf("") }
fun AddModificationItem(modification: Modification, onUpdate: (Modification) -> Unit) {
var isEditPriceBottomModalVisible by remember { mutableStateOf(false) }
if (isEditPriceBottomModalVisible) {
ModalBottomSheet(
onDismissRequest = {
isEditPriceBottomModalVisible = false
},
containerColor = Color.White,
sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
),
dragHandle = {},
scrimColor = Color.Transparent,
shape = RectangleShape
) {
EditPriceBottomModal {
isEditPriceBottomModalVisible = false
onUpdate(
modification.copy(price = it)
)
}
}
}
Column {
Box(
modifier = Modifier
@@ -74,14 +122,16 @@ fun AddModificationItem() {
.padding(vertical = 13.dp, horizontal = 16.dp),
) {
if (text.isEmpty()) {
) {
if (modification.name.isEmpty()) {
Text("Please enter the name", fontSize = 14.sp, color = Color(0xFFd6d6d6))
}
BasicTextField(
value = text,
value = modification.name,
onValueChange = {
text = it
onUpdate(
modification.copy(name = it)
)
},
modifier = Modifier
.fillMaxWidth()
@@ -94,13 +144,16 @@ fun AddModificationItem() {
.fillMaxWidth()
.background(Color.White)
.padding(top = 13.dp, bottom = 13.dp, start = 16.dp, end = 8.dp)
.clickable {
isEditPriceBottomModalVisible = true
}
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Text("Price", fontSize = 16.sp)
Spacer(modifier = Modifier.weight(1f))
Text("$74.00", fontSize = 16.sp, color = Color(0xffda3832))
Text(modification.price, fontSize = 16.sp, color = Color(0xffda3832))
Spacer(modifier = Modifier.width(6.dp))
Image(
painter = painterResource(id = R.drawable.rider_pro_nav_next),
@@ -113,7 +166,7 @@ fun AddModificationItem() {
}
@Composable
fun AddModificationButton() {
fun AddModificationButton(onClick: () -> Unit = {}) {
val stroke = Stroke(
width = 2f,
pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
@@ -125,6 +178,9 @@ fun AddModificationButton() {
.drawBehind {
drawRoundRect(color = Color(0xFFd6d6d6), style = stroke)
}
.clickable {
onClick()
}
) {
Image(
painter = painterResource(id = R.drawable.rider_pro_new_post_add_pic),
@@ -134,4 +190,48 @@ fun AddModificationButton() {
.align(Alignment.Center),
)
}
}
@Composable
fun EditPriceBottomModal(onOkClick: (String) -> Unit = {}) {
val focusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current
var text by remember { mutableStateOf("") }
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
// Modal content including BasicTextField
Row(
modifier = Modifier
.fillMaxWidth()
.background(Color.White)
.padding(16.dp)
) {
Text("Price", fontSize = 16.sp)
Spacer(modifier = Modifier.width(16.dp))
BasicTextField(
value = text,
onValueChange = { text = it },
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
singleLine = true,
keyboardActions = KeyboardActions(
onDone = {
keyboardController?.hide()
// Logic to close the dialog/modal
onOkClick(text)
}
),
)
}
}

View File

@@ -32,6 +32,7 @@ fun AddPage(){
.fillMaxSize()
.background(Color.Black)) {
AddBtn(icon = R.drawable.rider_pro_icon_rider_share, text = "Rider Share") {
NewPostViewModel.asNewPost()
navController.navigate("NewPost")
}
AddBtn(icon = R.drawable.rider_pro_location_create, text = "Location Create")

View File

@@ -2,6 +2,7 @@ package com.aiosman.riderpro
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -61,6 +62,7 @@ fun CommentsScreen() {
fun NoticeScreenHeader(
title:String
) {
val nav = LocalNavController.current
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
@@ -68,7 +70,9 @@ fun NoticeScreenHeader(
Image(
painter = painterResource(id = R.drawable.rider_pro_nav_back),
contentDescription = title,
modifier = Modifier.size(16.dp)
modifier = Modifier.size(16.dp).clickable {
nav.popBackStack()
}
)
Spacer(modifier = Modifier.size(12.dp))
Text(title, fontWeight = FontWeight.Bold, fontSize = 17.sp)

View File

@@ -0,0 +1,10 @@
package com.aiosman.riderpro
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
object IndexViewModel:ViewModel() {
var tabIndex by mutableStateOf(0)
}

View File

@@ -5,6 +5,7 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
@@ -35,6 +36,7 @@ import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -49,6 +51,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat
import androidx.navigation.NavGraph
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
@@ -85,28 +88,31 @@ fun NavigationController(navController: NavHostController) {
}
NavHost(
navController = navController,
startDestination = NavigationItem.Home.route,
startDestination = "Index",
) {
// 带底部导航栏的路由:
listOf(
NavigationItem.Home,
NavigationItem.Street,
NavigationItem.Add,
NavigationItem.Message,
NavigationItem.Profile
).forEach { item ->
composable(route = item.route) {
ScaffoldWithNavigationBar(navController) {
when (item) {
NavigationItem.Home -> Home()
NavigationItem.Street -> Street()
NavigationItem.Add -> Add()
NavigationItem.Message -> Video()
NavigationItem.Profile -> Profile()
else -> {} // 由于过滤,这里不会发生
}
}
}
// listOf(
// NavigationItem.Home,
// NavigationItem.Street,
// NavigationItem.Add,
// NavigationItem.Message,
// NavigationItem.Profile
// ).forEach { item ->
// composable(route = item.route) {
// ScaffoldWithNavigationBar(navController) {
// when (item) {
// NavigationItem.Home -> Home()
// NavigationItem.Street -> Street()
// NavigationItem.Add -> Add()
// NavigationItem.Message -> Video()
// NavigationItem.Profile -> Profile()
// else -> {} // 由于过滤,这里不会发生
// }
// }
// }
// }
composable(route = "Index") {
ScaffoldWithNavigationBar2()
}
composable(route = "ProfileTimeline") {
GalleryPage()
@@ -126,7 +132,7 @@ fun NavigationController(navController: NavHostController) {
}
composable(route = "Post") {
PostPage()
PostPage()
}
composable(route = "ModificationList") {
@@ -149,9 +155,7 @@ fun NavigationController(navController: NavHostController) {
FollowerPage()
}
composable(route = "NewPost") {
NewPostScreen()
}
composable(route = "EditModification") {
Box(
@@ -200,6 +204,11 @@ fun ScaffoldWithNavigationBar(
val currentRoute = navBackStackEntry?.destination?.route
val systemUiController = rememberSystemUiController()
item.forEach { it ->
val isSelected = currentRoute == it.route
val iconTint by animateColorAsState(
targetValue = if (isSelected) Color.Red else Color.White,
animationSpec = tween(durationMillis = 250), label = ""
)
NavigationBarItem(
selected = currentRoute == it.route,
onClick = {
@@ -232,20 +241,20 @@ fun ScaffoldWithNavigationBar(
}
},
colors = NavigationBarItemColors(
selectedIconColor = Color.Red,
selectedTextColor = Color.Red,
selectedIndicatorColor = Color.Black,
unselectedIconColor = Color.Red,
unselectedTextColor = Color.Red,
disabledIconColor = Color.Red,
disabledTextColor = Color.Red,
),
selectedIconColor = iconTint,
unselectedIconColor = iconTint,
),
icon = {
Icon(
modifier = Modifier.size(24.dp),
imageVector = it.icon(), contentDescription = null,
tint = if (currentRoute == it.route) Color.Red else Color.White
imageVector = if (currentRoute == it.route) it.selectedIcon() else it.icon(),
contentDescription = null,
tint = iconTint
)
}
)
@@ -263,10 +272,102 @@ fun ScaffoldWithNavigationBar(
}
@Composable
fun Home() {
fun ScaffoldWithNavigationBar2(
) {
val model = IndexViewModel
val navigationBarHeight = with(LocalDensity.current) {
WindowInsets.navigationBars.getBottom(this).toDp()
}
val item = listOf(
NavigationItem.Home,
NavigationItem.Street,
NavigationItem.Add,
NavigationItem.Message,
NavigationItem.Profile
)
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setNavigationBarColor(Color.Black)
}
Scaffold(
bottomBar = {
NavigationBar(
modifier = Modifier.height(56.dp + navigationBarHeight),
containerColor = Color.Black
) {
item.forEachIndexed { idx, it ->
val isSelected = model.tabIndex == idx
val iconTint by animateColorAsState(
targetValue = if (isSelected) Color.Red else Color.White,
animationSpec = tween(durationMillis = 250), label = ""
)
NavigationBarItem(
selected = isSelected,
onClick = {
model.tabIndex = idx
// if (it.route == NavigationItem.Add.route || it.route == NavigationItem.Message.route) {
// systemUiController.setStatusBarColor(Color.Black, darkIcons = false)
// } else {
// systemUiController.setStatusBarColor(
// Color.Transparent,
// darkIcons = true
// )
// }
},
colors = NavigationBarItemColors(
selectedTextColor = Color.Red,
selectedIndicatorColor = Color.Black,
unselectedTextColor = Color.Red,
disabledIconColor = Color.Red,
disabledTextColor = Color.Red,
selectedIconColor = iconTint,
unselectedIconColor = iconTint,
),
icon = {
Icon(
modifier = Modifier.size(24.dp),
imageVector = if (isSelected) it.selectedIcon() else it.icon(),
contentDescription = null,
tint = iconTint
)
}
)
}
}
}
) { innerPadding ->
Box(
modifier = Modifier
) {
when (model.tabIndex) {
0 -> Box(
modifier = Modifier.padding(innerPadding)
) { Home() }
1 -> Street()
2 -> Box(
modifier = Modifier.padding(innerPadding)
) { Add() }
3 -> Box(
modifier = Modifier.padding(innerPadding)
) { Video() }
4 -> Box(
modifier = Modifier.padding(innerPadding)
) { Profile() }
}
}
}
}
@Composable
fun Home() {
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Transparent, darkIcons = true)
}
Column(
modifier = Modifier
.fillMaxSize(),
@@ -280,8 +381,9 @@ fun Home() {
@Composable
fun Street() {
val navigationBarHeight = with(LocalDensity.current) {
WindowInsets.navigationBars.getBottom(this).toDp()
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Transparent, darkIcons = true)
}
Column(
modifier = Modifier
@@ -295,8 +397,9 @@ fun Street() {
@Composable
fun Add() {
val navigationBarHeight = with(LocalDensity.current) {
WindowInsets.navigationBars.getBottom(this).toDp()
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Black, darkIcons = false)
}
Column(
modifier = Modifier
@@ -311,8 +414,9 @@ fun Add() {
@Composable
fun Video() {
val navigationBarHeight = with(LocalDensity.current) {
WindowInsets.navigationBars.getBottom(this).toDp()
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Black, darkIcons = false)
}
Column(
modifier = Modifier
@@ -341,8 +445,9 @@ fun Message() {
@Composable
fun Profile() {
val navigationBarHeight = with(LocalDensity.current) {
WindowInsets.navigationBars.getBottom(this).toDp()
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Transparent, darkIcons = true)
}
Column(
modifier = Modifier

View File

@@ -9,15 +9,33 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
sealed class NavigationItem(val route:String, val icon: @Composable () -> ImageVector){
sealed class NavigationItem(
val route: String,
val icon: @Composable () -> ImageVector,
val selectedIcon: @Composable () -> ImageVector = icon
) {
data object Home : NavigationItem("Home",
{ ImageVector.vectorResource(R.drawable.rider_pro_home) })
icon = { ImageVector.vectorResource(R.drawable.rider_pro_home) },
selectedIcon = { ImageVector.vectorResource(R.drawable.rider_pro_home_filed) }
)
data object Street : NavigationItem("Street",
{ ImageVector.vectorResource(R.drawable.rider_pro_street) })
icon = { ImageVector.vectorResource(R.drawable.rider_pro_location) },
selectedIcon = { ImageVector.vectorResource(R.drawable.rider_pro_location_filed) }
)
data object Add : NavigationItem("Add",
{ ImageVector.vectorResource(R.drawable.rider_pro_moment_add) })
icon = { ImageVector.vectorResource(R.drawable.rider_pro_moment_add) },
selectedIcon = { ImageVector.vectorResource(R.drawable.rider_pro_moment_add) }
)
data object Message : NavigationItem("Message",
{ ImageVector.vectorResource(R.drawable.rider_pro_video) })
icon = { ImageVector.vectorResource(R.drawable.rider_pro_video_outline) },
selectedIcon = { ImageVector.vectorResource(R.drawable.rider_pro_video) }
)
data object Profile : NavigationItem("Profile",
{ ImageVector.vectorResource(R.drawable.rider_pro_profile) })
icon = { ImageVector.vectorResource(R.drawable.rider_pro_profile) },
selectedIcon = { ImageVector.vectorResource(R.drawable.rider_pro_profile_filed) }
)
}

View File

@@ -16,6 +16,7 @@ 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.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@@ -302,6 +303,7 @@ fun SelectedLocation(
.padding(end = 16.dp)
) {
Text(searchPlaceAddressResult.name, fontWeight = FontWeight.Bold)
Spacer(modifier = Modifier.height(4.dp))
Text(searchPlaceAddressResult.address, color = Color(0xFF9a9a9a))
}
Image(

View File

@@ -5,7 +5,15 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
object NewPostViewModel : ViewModel() {
var textContent by mutableStateOf("")
var searchPlaceAddressResult by mutableStateOf<SearchPlaceAddressResult?>(null)
var modificationList by mutableStateOf<List<Modification>>(listOf())
fun asNewPost() {
textContent = ""
searchPlaceAddressResult = null
modificationList = listOf()
}
}

View File

@@ -16,6 +16,8 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -27,8 +29,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@@ -124,6 +128,8 @@ fun LocationSearchTextInput(
onQueryClick: () -> Unit,
onValueChange: (String) -> Unit
) {
val keyboardController = LocalSoftwareKeyboardController.current
Box(
modifier = Modifier
.fillMaxWidth()
@@ -143,7 +149,6 @@ fun LocationSearchTextInput(
contentDescription = "Search",
modifier = Modifier
.size(24.dp)
.clickable { onQueryClick() }
)
Spacer(modifier = Modifier.width(8.dp))
if (value.isEmpty()) {
@@ -158,7 +163,18 @@ fun LocationSearchTextInput(
onValueChange = onValueChange,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp)
.padding(vertical = 16.dp),
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Search
),
keyboardActions = KeyboardActions(
onSearch = {
onQueryClick()
// hide keyboard
keyboardController?.hide()
}
)
)
}

View File

@@ -0,0 +1,10 @@
package com.aiosman.riderpro
object Utils {
fun generateRandomString(length: Int): String {
val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9')
return (1..length)
.map { allowedChars.random() }
.joinToString("")
}
}