diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c3888ba..8c973cc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -74,6 +74,7 @@ dependencies { // ZXing 二维码生成 implementation("com.google.zxing:core:3.5.2") + debugImplementation("com.squareup.leakcanary:leakcanary-android:2.12") diff --git a/app/src/main/java/com/example/smarthome/MainActivity.kt b/app/src/main/java/com/example/smarthome/MainActivity.kt index d2b6d01..5377337 100644 --- a/app/src/main/java/com/example/smarthome/MainActivity.kt +++ b/app/src/main/java/com/example/smarthome/MainActivity.kt @@ -134,7 +134,7 @@ fun MainContent() { } var selectedRoom by remember { mutableStateOf(0) } - var selectedNavItem by remember { mutableStateOf(0) } // 0=控制台, 5=设置 + var selectedNavItem by remember { mutableStateOf(0) } var rooms by remember { mutableStateOf(savedRooms) } // 保存房间列表到SharedPreferences @@ -170,26 +170,29 @@ fun MainContent() { .background(Color(overlayAlpha shl 24 or 0x121212)) ) - // 主内容 - 添加系统栏内边距 - Box(modifier = Modifier.fillMaxSize().systemBarsPadding()) { + // 主内容 - 全屏显示 + Box(modifier = Modifier.fillMaxSize()) { MainScaffold( - selectedRoom = selectedRoom, - onRoomSelect = { selectedRoom = it }, - selectedNavItem = selectedNavItem, - onNavItemSelect = { selectedNavItem = it }, - rooms = rooms, - onAddRoom = { newRoomName -> - saveRooms(rooms + newRoomName) - }, - onDeleteRoom = { index -> - if (rooms.size > 1) { // 至少保留一个房间 - saveRooms(rooms.filterIndexed { i, _ -> i != index }) - if (selectedRoom >= rooms.size - 1) { - selectedRoom = (rooms.size - 2).coerceAtLeast(0) + selectedRoom = selectedRoom, + onRoomSelect = { selectedRoom = it }, + selectedNavItem = selectedNavItem, + onNavItemSelect = { selectedNavItem = it }, + rooms = rooms, + onAddRoom = { newRoomName -> + saveRooms(rooms + newRoomName) + }, + onDeleteRoom = { index -> + if (rooms.size > 1) { // 至少保留一个房间 + saveRooms(rooms.filterIndexed { i, _ -> i != index }) + if (selectedRoom >= rooms.size - 1) { + selectedRoom = (rooms.size - 2).coerceAtLeast(0) + } } + }, + onRenameRoom = { index, newName -> + saveRooms(rooms.mapIndexed { i, name -> if (i == index) newName else name }) } - } - ) + ) } } } diff --git a/app/src/main/java/com/example/smarthome/ui/MainScaffold.kt b/app/src/main/java/com/example/smarthome/ui/MainScaffold.kt index b0c5424..bdbf4b0 100644 --- a/app/src/main/java/com/example/smarthome/ui/MainScaffold.kt +++ b/app/src/main/java/com/example/smarthome/ui/MainScaffold.kt @@ -36,6 +36,7 @@ import com.example.smarthome.data.LanguageManager import com.example.smarthome.data.tr import kotlinx.coroutines.launch import kotlinx.coroutines.delay +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalView import android.view.SoundEffectConstants @@ -47,6 +48,7 @@ import androidx.compose.foundation.gestures.detectHorizontalDragGestures import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.foundation.layout.offset import androidx.compose.ui.draw.rotate +import androidx.compose.ui.layout.ContentScale @Composable fun MainScaffold( @@ -56,27 +58,34 @@ fun MainScaffold( onNavItemSelect: (Int) -> Unit = {}, rooms: List = listOf("总览", "客厅", "厨房", "卧室", "影音室", "游戏房"), onAddRoom: (String) -> Unit = {}, - onDeleteRoom: (Int) -> Unit = {} + onDeleteRoom: (Int) -> Unit = {}, + onRenameRoom: (Int, String) -> Unit = { _, _ -> } ) { - Row(modifier = Modifier.fillMaxSize()) { - SideNavRail( - selectedNavItem = selectedNavItem, - onNavItemSelect = onNavItemSelect - ) - - // 内容区域 - 减少左侧间距 - Box(modifier = Modifier.fillMaxSize()) { + Box(modifier = Modifier.fillMaxSize()) { + // 内容区域 - 全屏显示 + Box( + modifier = Modifier.fillMaxSize() + ) { // 根据选中的导航项显示不同的内容 + // 底部导航: 0-Dashboard, 1-Scene, 2-Automation, 3-Security, 4-Settings when (selectedNavItem) { - 0 -> DashboardContent(selectedRoom, onRoomSelect, rooms, onAddRoom, onDeleteRoom) + 0 -> DashboardContent(selectedRoom, onRoomSelect, rooms, onAddRoom, onDeleteRoom, onRenameRoom) 1 -> SceneScreen() 2 -> AutomationScreen() - 3 -> StatisticsScreen() - 4 -> SecurityScreen() - 5 -> SettingsContent() - else -> DashboardContent(selectedRoom, onRoomSelect, rooms, onAddRoom, onDeleteRoom) + 3 -> SecurityScreen() + 4 -> SettingsContent() + else -> DashboardContent(selectedRoom, onRoomSelect, rooms, onAddRoom, onDeleteRoom, onRenameRoom) } } + + // 底部悬浮导航栏 - 置于内容之上 + FloatingBottomNav( + selectedNavItem = selectedNavItem, + onNavItemSelect = onNavItemSelect, + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 24.dp) + ) } } @@ -86,23 +95,33 @@ fun DashboardContent( onRoomSelect: (Int) -> Unit, rooms: List = listOf("总览", "客厅", "厨房", "卧室", "影音室", "游戏房"), onAddRoom: (String) -> Unit = {}, - onDeleteRoom: (Int) -> Unit = {} + onDeleteRoom: (Int) -> Unit = {}, + onRenameRoom: (Int, String) -> Unit = { _, _ -> } ) { var showAddRoomDialog by remember { mutableStateOf(false) } - var editMode by remember { mutableStateOf(false) } + + // 总览和客厅使用默认壁纸 + val roomName = rooms.getOrNull(selectedRoom) ?: "总览" + val hasWallpaper = selectedRoom == 0 || roomName == "客厅" Box( modifier = Modifier .fillMaxSize() - .pointerInput(editMode) { - if (editMode) { - detectTapGestures( - onTap = { editMode = false } - ) - } - } ) { - Column(modifier = Modifier.fillMaxSize().padding(start = 0.dp, end = 16.dp, top = 16.dp, bottom = 16.dp)) { + // 房间壁纸背景(总览和客厅)- 放在最底层 + if (hasWallpaper) { + Image( + painter = painterResource(id = R.drawable.room_wallpaper_default), + contentDescription = null, + modifier = Modifier + .fillMaxSize() + .clip(RoundedCornerShape(24.dp)), + contentScale = ContentScale.Crop, + alpha = 0.25f + ) + } + + Column(modifier = Modifier.fillMaxSize().padding(horizontal = 16.dp, vertical = 16.dp)) { TopBar() RoomTabs( selectedRoom = selectedRoom, @@ -110,11 +129,10 @@ fun DashboardContent( rooms = rooms, onAddRoomClick = { showAddRoomDialog = true }, onDeleteRoom = onDeleteRoom, - editMode = editMode, - onEditModeChange = { editMode = it } + onRenameRoom = onRenameRoom ) // 根据选中的房间显示不同的内容 - RoomContent(selectedRoom = selectedRoom, roomName = rooms.getOrNull(selectedRoom) ?: "总览") + RoomContent(selectedRoom = selectedRoom, roomName = roomName) } if (showAddRoomDialog) { @@ -288,9 +306,11 @@ fun RoomTabs( rooms: List = listOf("总览", "客厅", "厨房", "卧室", "影音室", "游戏房"), onAddRoomClick: () -> Unit = {}, onDeleteRoom: (Int) -> Unit = {}, - editMode: Boolean = false, - onEditModeChange: (Boolean) -> Unit = {} + onRenameRoom: (Int, String) -> Unit = { _, _ -> } ) { + // 编辑房间对话框状态 + var showEditRoomsDialog by remember { mutableStateOf(false) } + Row( modifier = Modifier .fillMaxWidth() @@ -308,24 +328,40 @@ fun RoomTabs( RoomTab( name = name, selected = index == selectedRoom, - editMode = editMode, - canDelete = rooms.size > 1, - onClick = { - if (!editMode) { - onRoomSelect(index) - } - }, - onLongClick = { onEditModeChange(true) }, - onDelete = { - onDeleteRoom(index) - onEditModeChange(false) - } + onClick = { onRoomSelect(index) } ) } } - // 固定的添加按钮 - GradientButton("+ 添加房间", onClick = onAddRoomClick) + // 编辑按钮 + Box( + modifier = Modifier + .size(44.dp) + .clip(RoundedCornerShape(12.dp)) + .background(Color(0xFF2A2A3E)) + .border( + width = 1.dp, + color = Color(0xFF3A3A4E), + shape = RoundedCornerShape(12.dp) + ) + .clickable { showEditRoomsDialog = true }, + contentAlignment = Alignment.Center + ) { + Text(text = "✏️", fontSize = 18.sp) + } + + // 添加按钮 + GradientButton("+ 添加", onClick = onAddRoomClick) + } + + // 编辑房间对话框 + if (showEditRoomsDialog) { + EditRoomsDialog( + rooms = rooms, + onDismiss = { showEditRoomsDialog = false }, + onRenameRoom = onRenameRoom, + onDeleteRoom = onDeleteRoom + ) } } @@ -333,98 +369,25 @@ fun RoomTabs( fun RoomTab( name: String, selected: Boolean, - editMode: Boolean, - canDelete: Boolean, - onClick: () -> Unit, - onLongClick: () -> Unit, - onDelete: () -> Unit + onClick: () -> Unit ) { - val scope = rememberCoroutineScope() - - // 改进的晃动动画 - 使用无限循环的旋转动画 - val infiniteTransition = rememberInfiniteTransition(label = "shake") - val rotation by infiniteTransition.animateFloat( - initialValue = -3f, - targetValue = 3f, - animationSpec = infiniteRepeatable( - animation = tween(150, easing = LinearEasing), - repeatMode = RepeatMode.Reverse - ), - label = "rotation" - ) - - // 缩放动画 - val scale by androidx.compose.animation.core.animateFloatAsState( - targetValue = if (editMode && canDelete) 0.95f else 1f, - animationSpec = androidx.compose.animation.core.spring( - dampingRatio = androidx.compose.animation.core.Spring.DampingRatioMediumBouncy, - stiffness = androidx.compose.animation.core.Spring.StiffnessLow - ), - label = "scale" - ) + val base = Modifier.clip(RoundedCornerShape(20.dp)) + val mod = if (selected) { + base.background(brush = Brush.linearGradient(listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF)))) + } else { + base.background(color = Color(0x50121212)) + } Box( - modifier = Modifier - .scale(scale) - .then( - if (editMode && canDelete) { - Modifier.rotate(rotation) - } else { - Modifier - } - ) - .pointerInput(Unit) { - detectTapGestures( - onTap = { onClick() }, - onLongPress = { - if (canDelete) { - onLongClick() - } - } - ) - } + modifier = mod + .clickable { onClick() } + .padding(horizontal = 14.dp, vertical = 10.dp) ) { - val base = Modifier.clip(RoundedCornerShape(20.dp)) - val mod = if (selected) { - base.background(brush = Brush.linearGradient(listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF)))) - } else { - base.background(color = Color(0x50121212)) - } - - Box( - modifier = mod.padding(horizontal = 14.dp, vertical = 10.dp) - ) { - Text( - text = name, - color = if (selected) Color.Black else Color(0xFFB0B0B0), - maxLines = 1 - ) - } - - // 删除按钮 - if (editMode && canDelete) { - Box( - modifier = Modifier - .size(22.dp) - .offset(x = (-4).dp, y = (-4).dp) - .align(Alignment.TopEnd) - .shadow(4.dp, RoundedCornerShape(11.dp)) - .clip(RoundedCornerShape(11.dp)) - .background(Color(0xFFFF5252)) - .clickable( - indication = null, - interactionSource = remember { MutableInteractionSource() } - ) { onDelete() }, - contentAlignment = Alignment.Center - ) { - Text( - text = "×", - color = Color.White, - fontSize = 18.sp, - fontWeight = FontWeight.Bold - ) - } - } + Text( + text = name, + color = if (selected) Color.Black else Color(0xFFB0B0B0), + maxLines = 1 + ) } } @@ -573,6 +536,177 @@ fun NavItem(title: String, selected: Boolean) { } } +// 底部悬浮导航栏 - 带液态玻璃滑动效果 +@Composable +fun FloatingBottomNav( + selectedNavItem: Int = 0, + onNavItemSelect: (Int) -> Unit = {}, + modifier: Modifier = Modifier +) { + // 监听语言变化以触发重组 + val currentLang by LanguageManager.currentLanguage.collectAsState() + + val navItems = listOf( + R.drawable.ic_dashboard, + R.drawable.ic_scene, + R.drawable.ic_automation, + R.drawable.ic_security, + R.drawable.ic_settings + ) + + val itemWidth = 52.dp + val itemSpacing = 4.dp + val totalItemWidth = itemWidth + itemSpacing + + // 是否正在拖动 + var isDragging by remember { mutableStateOf(false) } + // 拖动时的临时选中项 + var dragIndex by remember { mutableStateOf(selectedNavItem) } + + // 当外部 selectedNavItem 变化时,同步更新 dragIndex + LaunchedEffect(selectedNavItem) { + if (!isDragging) { + dragIndex = selectedNavItem + } + } + + // 选中框的动画偏移量 - 跟随 dragIndex(拖动时)或 selectedNavItem(点击时) + val currentIndex = if (isDragging) dragIndex else selectedNavItem + val animatedOffset by animateFloatAsState( + targetValue = currentIndex * (itemWidth.value + itemSpacing.value), + animationSpec = spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = Spring.StiffnessHigh + ), + label = "liquid_glass_offset" + ) + + + val density = LocalDensity.current + + // 菜单栏高度(胶囊形) + val navBarHeight = 68.dp + val navBarPadding = 10.dp + val innerHeight = navBarHeight - navBarPadding * 2 // 48dp + + // 选中指示器尺寸 - 按住时放大到贴合菜单栏边缘 + val indicatorSize by animateFloatAsState( + targetValue = if (isDragging) (navBarHeight.value - 4f) else innerHeight.value, + animationSpec = spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = Spring.StiffnessMedium + ), + label = "indicator_size" + ) + + Box( + modifier = modifier + .height(navBarHeight) + .shadow(24.dp, RoundedCornerShape(50)) + .clip(RoundedCornerShape(50)) + .background( + Brush.linearGradient( + colors = listOf( + Color(0xFF1A1A2E).copy(alpha = 0.95f), + Color(0xFF16213E).copy(alpha = 0.95f) + ) + ) + ) + .border( + width = 1.dp, + brush = Brush.linearGradient( + colors = listOf( + Color.White.copy(alpha = 0.1f), + Color.White.copy(alpha = 0.05f) + ) + ), + shape = RoundedCornerShape(50) + ) + .padding(horizontal = 10.dp, vertical = 10.dp) + .pointerInput(Unit) { + detectHorizontalDragGestures( + onDragStart = { offset -> + isDragging = true + val index = (offset.x / (itemWidth.toPx() + itemSpacing.toPx())).toInt() + .coerceIn(0, navItems.size - 1) + dragIndex = index + }, + onDragEnd = { + isDragging = false + onNavItemSelect(dragIndex) + }, + onDragCancel = { + isDragging = false + dragIndex = selectedNavItem + }, + onHorizontalDrag = { change, _ -> + change.consume() + val index = (change.position.x / (itemWidth.toPx() + itemSpacing.toPx())).toInt() + .coerceIn(0, navItems.size - 1) + if (index != dragIndex) { + dragIndex = index + } + } + ) + } + ) { + Box( + contentAlignment = Alignment.CenterStart + ) { + // 选中指示器 - 按住时放大贴合菜单栏边缘 + val horizontalOffset = (itemWidth.value - indicatorSize) / 2f + + Box( + modifier = Modifier + .align(Alignment.CenterStart) + .offset(x = animatedOffset.dp + horizontalOffset.dp) + .size(indicatorSize.dp) + .clip(RoundedCornerShape(50)) + .background(Color.White.copy(alpha = if (isDragging) 0.2f else 0.12f)) + .border( + width = if (isDragging) 2.dp else 1.5.dp, + color = Color.White.copy(alpha = if (isDragging) 0.6f else 0.25f), + shape = RoundedCornerShape(50) + ) + ) + + // 图标层 + Row( + horizontalArrangement = Arrangement.spacedBy(itemSpacing), + verticalAlignment = Alignment.CenterVertically + ) { + navItems.forEachIndexed { index, iconRes -> + val isCurrentSelected = if (isDragging) index == dragIndex else index == selectedNavItem + + Box( + modifier = Modifier + .size(itemWidth) + .clip(RoundedCornerShape(16.dp)) + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { + if (!isDragging) { + onNavItemSelect(index) + } + }, + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource(id = iconRes), + contentDescription = null, + modifier = Modifier.size(24.dp), + colorFilter = androidx.compose.ui.graphics.ColorFilter.tint( + if (isCurrentSelected) Color.White else Color(0xFFB0B0B0) + ) + ) + } + } + } + } + } +} + @Composable fun SideNavRail(selectedNavItem: Int = 0, onNavItemSelect: (Int) -> Unit = {}) { val context = androidx.compose.ui.platform.LocalContext.current @@ -2501,3 +2635,432 @@ fun AddRoomDialog( } } } + +@Composable +fun RenameRoomDialog( + currentName: String, + onDismiss: () -> Unit, + onConfirm: (String) -> Unit +) { + var roomName by remember { mutableStateOf(currentName) } + + // 背景遮罩(点击关闭) + Box( + modifier = Modifier + .fillMaxSize() + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { onDismiss() }, + contentAlignment = Alignment.Center + ) { + // 对话框主体 + Box( + modifier = Modifier + .width(380.dp) + .clip(RoundedCornerShape(28.dp)) + .background( + Brush.verticalGradient( + listOf( + Color(0xFF2A2A3E), + Color(0xFF1A1A2E) + ) + ) + ) + .border( + width = 1.dp, + brush = Brush.linearGradient( + listOf(Color(0xFF00D1FF).copy(alpha = 0.3f), Color(0xFFB89CFF).copy(alpha = 0.3f)) + ), + shape = RoundedCornerShape(28.dp) + ) + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { /* 阻止点击穿透 */ } + .padding(28.dp) + ) { + Column( + verticalArrangement = Arrangement.spacedBy(24.dp) + ) { + // 标题区域 + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Box( + modifier = Modifier + .size(44.dp) + .clip(RoundedCornerShape(12.dp)) + .background( + Brush.linearGradient( + listOf(Color(0xFF00D1FF).copy(alpha = 0.3f), Color(0xFFB89CFF).copy(alpha = 0.3f)) + ) + ), + contentAlignment = Alignment.Center + ) { + Text(text = "✏️", fontSize = 22.sp) + } + Column { + Text( + text = "重命名房间", + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + color = Color.White + ) + Text( + text = "修改房间名称", + fontSize = 12.sp, + color = Color(0xFF9AA0A6) + ) + } + } + + Box( + modifier = Modifier + .size(32.dp) + .clip(RoundedCornerShape(8.dp)) + .background(Color(0x33FFFFFF)) + .clickable { onDismiss() }, + contentAlignment = Alignment.Center + ) { + Text(text = "✕", color = Color(0xFF9AA0A6), fontSize = 14.sp) + } + } + + // 房间名称输入 + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + Text( + text = "房间名称", + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFFB0B0B0) + ) + TextField( + value = roomName, + onValueChange = { roomName = it }, + placeholder = { Text("输入新名称...", color = Color(0xFF6A6A7A)) }, + modifier = Modifier + .fillMaxWidth() + .height(56.dp), + colors = TextFieldDefaults.colors( + focusedContainerColor = Color(0xFF1A1A2E), + unfocusedContainerColor = Color(0xFF1A1A2E), + focusedTextColor = Color.White, + unfocusedTextColor = Color.White, + cursorColor = Color(0xFF00D1FF), + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent + ), + shape = RoundedCornerShape(14.dp), + singleLine = true, + textStyle = androidx.compose.ui.text.TextStyle(fontSize = 16.sp) + ) + } + + // 按钮区域 + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Box( + modifier = Modifier + .weight(1f) + .height(52.dp) + .clip(RoundedCornerShape(14.dp)) + .background(Color(0xFF1A1A2E)) + .border( + width = 1.dp, + color = Color(0xFF3A3A4E), + shape = RoundedCornerShape(14.dp) + ) + .clickable { onDismiss() }, + contentAlignment = Alignment.Center + ) { + Text( + text = "取消", + color = Color(0xFFB0B0B0), + fontWeight = FontWeight.Medium, + fontSize = 15.sp + ) + } + + val canConfirm = roomName.isNotBlank() && roomName.trim() != currentName + Box( + modifier = Modifier + .weight(1f) + .height(52.dp) + .clip(RoundedCornerShape(14.dp)) + .background( + if (canConfirm) + Brush.linearGradient(listOf(Color(0xFF00D1FF), Color(0xFFB89CFF))) + else + Brush.linearGradient(listOf(Color(0xFF3A3A4E), Color(0xFF3A3A4E))) + ) + .clickable(enabled = canConfirm) { + onConfirm(roomName.trim()) + }, + contentAlignment = Alignment.Center + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = "✓", + color = if (canConfirm) Color.White else Color(0xFF6A6A7A), + fontSize = 16.sp + ) + Text( + text = "确认修改", + color = if (canConfirm) Color.White else Color(0xFF6A6A7A), + fontWeight = FontWeight.Bold, + fontSize = 15.sp + ) + } + } + } + } + } + } +} + +@Composable +fun EditRoomsDialog( + rooms: List, + onDismiss: () -> Unit, + onRenameRoom: (Int, String) -> Unit, + onDeleteRoom: (Int) -> Unit +) { + // 当前编辑的房间索引 + var editingIndex by remember { mutableStateOf(-1) } + var editingName by remember { mutableStateOf("") } + + // 背景遮罩 + Box( + modifier = Modifier + .fillMaxSize() + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { onDismiss() }, + contentAlignment = Alignment.Center + ) { + // 对话框主体 + Box( + modifier = Modifier + .width(420.dp) + .clip(RoundedCornerShape(28.dp)) + .background( + Brush.verticalGradient( + listOf( + Color(0xFF2A2A3E), + Color(0xFF1A1A2E) + ) + ) + ) + .border( + width = 1.dp, + brush = Brush.linearGradient( + listOf(Color(0xFF00D1FF).copy(alpha = 0.3f), Color(0xFFB89CFF).copy(alpha = 0.3f)) + ), + shape = RoundedCornerShape(28.dp) + ) + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { /* 阻止点击穿透 */ } + .padding(28.dp) + ) { + Column( + verticalArrangement = Arrangement.spacedBy(20.dp) + ) { + // 标题区域 + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Box( + modifier = Modifier + .size(44.dp) + .clip(RoundedCornerShape(12.dp)) + .background( + Brush.linearGradient( + listOf(Color(0xFF00D1FF).copy(alpha = 0.3f), Color(0xFFB89CFF).copy(alpha = 0.3f)) + ) + ), + contentAlignment = Alignment.Center + ) { + Text(text = "🏠", fontSize = 22.sp) + } + Column { + Text( + text = "编辑房间", + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + color = Color.White + ) + Text( + text = "管理你的房间列表", + fontSize = 12.sp, + color = Color(0xFF9AA0A6) + ) + } + } + + Box( + modifier = Modifier + .size(32.dp) + .clip(RoundedCornerShape(8.dp)) + .background(Color(0x33FFFFFF)) + .clickable { onDismiss() }, + contentAlignment = Alignment.Center + ) { + Text(text = "✕", color = Color(0xFF9AA0A6), fontSize = 14.sp) + } + } + + // 房间列表 + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.heightIn(max = 300.dp).verticalScroll(rememberScrollState()) + ) { + rooms.forEachIndexed { index, name -> + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(14.dp)) + .background(Color(0xFF1A1A2E)) + .padding(12.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + if (editingIndex == index) { + // 编辑模式 + TextField( + value = editingName, + onValueChange = { editingName = it }, + modifier = Modifier + .weight(1f) + .height(48.dp), + colors = TextFieldDefaults.colors( + focusedContainerColor = Color(0xFF2A2A3E), + unfocusedContainerColor = Color(0xFF2A2A3E), + focusedTextColor = Color.White, + unfocusedTextColor = Color.White, + cursorColor = Color(0xFF00D1FF), + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent + ), + shape = RoundedCornerShape(10.dp), + singleLine = true, + textStyle = androidx.compose.ui.text.TextStyle(fontSize = 15.sp) + ) + + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + // 确认按钮 + Box( + modifier = Modifier + .size(36.dp) + .clip(RoundedCornerShape(8.dp)) + .background(Color(0xFF4CAF50)) + .clickable { + if (editingName.isNotBlank() && editingName != name) { + onRenameRoom(index, editingName.trim()) + } + editingIndex = -1 + }, + contentAlignment = Alignment.Center + ) { + Text(text = "✓", color = Color.White, fontSize = 16.sp) + } + // 取消按钮 + Box( + modifier = Modifier + .size(36.dp) + .clip(RoundedCornerShape(8.dp)) + .background(Color(0xFF3A3A4E)) + .clickable { editingIndex = -1 }, + contentAlignment = Alignment.Center + ) { + Text(text = "✕", color = Color(0xFFB0B0B0), fontSize = 16.sp) + } + } + } else { + // 显示模式 + Text( + text = name, + color = Color.White, + fontSize = 15.sp, + modifier = Modifier.weight(1f) + ) + + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + // 编辑按钮 + Box( + modifier = Modifier + .size(36.dp) + .clip(RoundedCornerShape(8.dp)) + .background(Color(0xFF3A3A4E)) + .clickable { + editingIndex = index + editingName = name + }, + contentAlignment = Alignment.Center + ) { + Text(text = "✏️", fontSize = 16.sp) + } + // 删除按钮 + if (rooms.size > 1) { + Box( + modifier = Modifier + .size(36.dp) + .clip(RoundedCornerShape(8.dp)) + .background(Color(0xFFFF5252).copy(alpha = 0.2f)) + .clickable { onDeleteRoom(index) }, + contentAlignment = Alignment.Center + ) { + Text(text = "🗑️", fontSize = 16.sp) + } + } + } + } + } + } + } + + // 完成按钮 + Box( + modifier = Modifier + .fillMaxWidth() + .height(52.dp) + .clip(RoundedCornerShape(14.dp)) + .background( + Brush.linearGradient(listOf(Color(0xFF00D1FF), Color(0xFFB89CFF))) + ) + .clickable { onDismiss() }, + contentAlignment = Alignment.Center + ) { + Text( + text = "完成", + color = Color.White, + fontWeight = FontWeight.Bold, + fontSize = 15.sp + ) + } + } + } + } +} diff --git a/app/src/main/res/drawable/room_wallpaper_default.png b/app/src/main/res/drawable/room_wallpaper_default.png new file mode 100644 index 0000000..fbdbe16 Binary files /dev/null and b/app/src/main/res/drawable/room_wallpaper_default.png differ diff --git a/zenhome-wallpaper-1764392320729.png b/zenhome-wallpaper-1764392320729.png new file mode 100644 index 0000000..fbdbe16 Binary files /dev/null and b/zenhome-wallpaper-1764392320729.png differ