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 e495955..def2105 100644 --- a/app/src/main/java/com/example/smarthome/ui/MainScaffold.kt +++ b/app/src/main/java/com/example/smarthome/ui/MainScaffold.kt @@ -49,6 +49,8 @@ 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 +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.ui.graphics.SolidColor @Composable fun MainScaffold( @@ -61,6 +63,9 @@ fun MainScaffold( onDeleteRoom: (Int) -> Unit = {}, onRenameRoom: (Int, String) -> Unit = { _, _ -> } ) { + // 编辑房间对话框状态 - 提升到这里以控制菜单栏显示 + var showEditRoomsDialog by remember { mutableStateOf(false) } + Box(modifier = Modifier.fillMaxSize()) { // 内容区域 - 全屏显示 Box( @@ -69,23 +74,59 @@ fun MainScaffold( // 根据选中的导航项显示不同的内容 // 底部导航: 0-Dashboard, 1-Scene, 2-Automation, 3-Security, 4-Settings when (selectedNavItem) { - 0 -> DashboardContent(selectedRoom, onRoomSelect, rooms, onAddRoom, onDeleteRoom, onRenameRoom) + 0 -> DashboardContent( + selectedRoom = selectedRoom, + onRoomSelect = onRoomSelect, + rooms = rooms, + onAddRoom = onAddRoom, + onDeleteRoom = onDeleteRoom, + onRenameRoom = onRenameRoom, + showEditRoomsDialog = showEditRoomsDialog, + onShowEditRoomsDialog = { showEditRoomsDialog = it } + ) 1 -> SceneScreen() 2 -> AutomationScreen() 3 -> SecurityScreen() 4 -> SettingsContent() - else -> DashboardContent(selectedRoom, onRoomSelect, rooms, onAddRoom, onDeleteRoom, onRenameRoom) + else -> DashboardContent( + selectedRoom = selectedRoom, + onRoomSelect = onRoomSelect, + rooms = rooms, + onAddRoom = onAddRoom, + onDeleteRoom = onDeleteRoom, + onRenameRoom = onRenameRoom, + showEditRoomsDialog = showEditRoomsDialog, + onShowEditRoomsDialog = { showEditRoomsDialog = it } + ) } } - // 底部悬浮导航栏 - 置于内容之上 - FloatingBottomNav( - selectedNavItem = selectedNavItem, - onNavItemSelect = onNavItemSelect, + // 底部渐变遮罩 - 让内容优雅淡出 + Box( modifier = Modifier .align(Alignment.BottomCenter) - .padding(bottom = 24.dp) + .fillMaxWidth() + .height(120.dp) + .background( + Brush.verticalGradient( + colors = listOf( + Color.Transparent, + Color(0xFF0A0A0A).copy(alpha = 0.8f) + ) + ) + ) ) + + // 底部悬浮导航栏 - 置于内容之上,编辑房间时隐藏 + if (!showEditRoomsDialog) { + FloatingBottomNav( + selectedNavItem = selectedNavItem, + onNavItemSelect = onNavItemSelect, + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 24.dp) + ) + } } } @@ -96,28 +137,58 @@ fun DashboardContent( rooms: List = listOf("总览", "客厅", "厨房", "卧室", "影音室", "游戏房"), onAddRoom: (String) -> Unit = {}, onDeleteRoom: (Int) -> Unit = {}, - onRenameRoom: (Int, String) -> Unit = { _, _ -> } + onRenameRoom: (Int, String) -> Unit = { _, _ -> }, + showEditRoomsDialog: Boolean = false, + onShowEditRoomsDialog: (Boolean) -> Unit = {} ) { var showAddRoomDialog by remember { mutableStateOf(false) } - // 总览和客厅使用默认壁纸 + // 根据房间选择壁纸 val roomName = rooms.getOrNull(selectedRoom) ?: "总览" - val hasWallpaper = selectedRoom == 0 || roomName == "客厅" + val wallpaperRes = when { + selectedRoom == 0 -> R.drawable.room_wallpaper_default // 总览 + roomName == "客厅" -> R.drawable.room_wallpaper_default + roomName == "厨房" -> R.drawable.zenhome_wallpaper_1764420980064 + roomName == "卧室" -> R.drawable.zenhome_wallpaper_1764420805752 + else -> null + } Box( modifier = Modifier .fillMaxSize() ) { - // 房间壁纸背景(总览和客厅)- 放在最底层 - if (hasWallpaper) { + // 房间壁纸背景 - 放在最底层 + if (wallpaperRes != null) { + // 先添加深色底层,避免与全局背景重叠 + Box( + modifier = Modifier + .fillMaxSize() + .clip(RoundedCornerShape(24.dp)) + .background(Color(0xFF0D0D0D)) + ) Image( - painter = painterResource(id = R.drawable.room_wallpaper_default), + painter = painterResource(id = wallpaperRes), contentDescription = null, modifier = Modifier .fillMaxSize() .clip(RoundedCornerShape(24.dp)), contentScale = ContentScale.Crop, - alpha = 0.25f + alpha = 0.6f + ) + // 添加渐变遮罩层增强可读性 + Box( + modifier = Modifier + .fillMaxSize() + .clip(RoundedCornerShape(24.dp)) + .background( + Brush.verticalGradient( + colors = listOf( + Color(0x99000000), + Color(0x66000000), + Color(0x99000000) + ) + ) + ) ) } @@ -129,7 +200,9 @@ fun DashboardContent( rooms = rooms, onAddRoomClick = { showAddRoomDialog = true }, onDeleteRoom = onDeleteRoom, - onRenameRoom = onRenameRoom + onRenameRoom = onRenameRoom, + showEditRoomsDialog = showEditRoomsDialog, + onShowEditRoomsDialog = onShowEditRoomsDialog ) // 根据选中的房间显示不同的内容 RoomContent(selectedRoom = selectedRoom, roomName = roomName) @@ -163,12 +236,18 @@ fun OverviewRoomContent() { .fillMaxSize() .verticalScroll(rememberScrollState()) ) { - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(16.dp)) { - AirConditionerCard(modifier = Modifier.weight(1f), roomName = tr("room_all")) - UsageStatusChart(modifier = Modifier.weight(1f), roomName = tr("room_all")) + Row( + modifier = Modifier + .fillMaxWidth() + .height(IntrinsicSize.Max), + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + AirConditionerCard(modifier = Modifier.weight(1f).fillMaxHeight(), roomName = tr("room_all")) + UsageStatusChart(modifier = Modifier.weight(1f).fillMaxHeight(), roomName = tr("room_all")) } - LightRow(modifier = Modifier.fillMaxWidth().padding(top = 16.dp, bottom = 16.dp), roomName = tr("room_all")) + Spacer(modifier = Modifier.height(16.dp)) ModeButtonsRow(onModeSelected = { }) + LightRow(modifier = Modifier.fillMaxWidth().padding(top = 16.dp, bottom = 16.dp), roomName = tr("room_all")) Spacer(modifier = Modifier.height(24.dp)) @@ -181,6 +260,9 @@ fun OverviewRoomContent() { modifier = Modifier.padding(bottom = 12.dp) ) AllDevicesOverview(modifier = Modifier.fillMaxWidth()) + + // 底部留白,避免被悬浮菜单栏遮挡 + Spacer(modifier = Modifier.height(100.dp)) } } @@ -207,6 +289,9 @@ fun SpecificRoomContent(selectedRoom: Int, roomName: String) { modifier = Modifier.padding(bottom = 12.dp) ) MyDevicesGrid(selectedRoom = selectedRoom, modifier = Modifier.fillMaxWidth()) + + // 底部留白,避免被悬浮菜单栏遮挡 + Spacer(modifier = Modifier.height(100.dp)) } } @@ -306,10 +391,10 @@ fun RoomTabs( rooms: List = listOf("总览", "客厅", "厨房", "卧室", "影音室", "游戏房"), onAddRoomClick: () -> Unit = {}, onDeleteRoom: (Int) -> Unit = {}, - onRenameRoom: (Int, String) -> Unit = { _, _ -> } + onRenameRoom: (Int, String) -> Unit = { _, _ -> }, + showEditRoomsDialog: Boolean = false, + onShowEditRoomsDialog: (Boolean) -> Unit = {} ) { - // 编辑房间对话框状态 - var showEditRoomsDialog by remember { mutableStateOf(false) } Row( modifier = Modifier @@ -333,34 +418,45 @@ fun RoomTabs( } } - // 编辑按钮 + // 管理按钮(三条横线) Box( modifier = Modifier .size(44.dp) .clip(RoundedCornerShape(12.dp)) - .background(Color(0xFF2A2A3E)) + .background(Color.White.copy(alpha = 0.15f)) .border( width = 1.dp, - color = Color(0xFF3A3A4E), + color = Color.White.copy(alpha = 0.25f), shape = RoundedCornerShape(12.dp) ) - .clickable { showEditRoomsDialog = true }, + .clickable { onShowEditRoomsDialog(true) }, contentAlignment = Alignment.Center ) { - Text(text = "✏️", fontSize = 18.sp) + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + repeat(3) { + Box( + modifier = Modifier + .width(18.dp) + .height(2.dp) + .clip(RoundedCornerShape(1.dp)) + .background(Color.White.copy(alpha = 0.8f)) + ) + } + } } - - // 添加按钮 - GradientButton("+ 添加", onClick = onAddRoomClick) } // 编辑房间对话框 if (showEditRoomsDialog) { EditRoomsDialog( rooms = rooms, - onDismiss = { showEditRoomsDialog = false }, + onDismiss = { onShowEditRoomsDialog(false) }, onRenameRoom = onRenameRoom, - onDeleteRoom = onDeleteRoom + onDeleteRoom = onDeleteRoom, + onAddRoom = onAddRoomClick ) } } @@ -378,13 +474,13 @@ fun RoomTab( .clip(shape) .then( if (selected) { - Modifier.background(brush = Brush.linearGradient(listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF)))) + Modifier.background(Color.White.copy(alpha = 0.95f)) } else { Modifier - .background(Color(0xFF1E2530).copy(alpha = 0.8f)) + .background(Color.White.copy(alpha = 0.12f)) .border( width = 1.dp, - color = Color.White.copy(alpha = 0.15f), + color = Color.White.copy(alpha = 0.2f), shape = shape ) } @@ -394,7 +490,8 @@ fun RoomTab( ) { Text( text = name, - color = if (selected) Color.Black else Color(0xFFB0B0B0), + color = if (selected) Color(0xFF1A1A1A) else Color.White.copy(alpha = 0.85f), + fontWeight = if (selected) FontWeight.SemiBold else FontWeight.Normal, maxLines = 1 ) } @@ -426,7 +523,7 @@ fun DeviceCard(d: Device, roomName: String = "") { modifier = Modifier .fillMaxWidth() .clip(RoundedCornerShape(16.dp)) - .background(Color(0xFF1E2530).copy(alpha = 0.85f)) + .background(Color(0xFF1A1A1A).copy(alpha = 0.65f)) .border( width = 1.dp, color = Color.White.copy(alpha = 0.15f), @@ -550,7 +647,7 @@ fun NavItem(title: String, selected: Boolean) { } } -// 底部悬浮导航栏 - 带液态玻璃滑动效果 +// 底部悬浮导航栏 - 带液态玻璃滑动效果和呼吸效果 @Composable fun FloatingBottomNav( selectedNavItem: Int = 0, @@ -577,6 +674,23 @@ fun FloatingBottomNav( // 拖动时的临时选中项 var dragIndex by remember { mutableStateOf(selectedNavItem) } + // 呼吸效果:不操作时变透明 + var lastInteractionTime by remember { mutableStateOf(System.currentTimeMillis()) } + var isIdle by remember { mutableStateOf(false) } + + // 检测空闲状态(3秒无操作) + LaunchedEffect(lastInteractionTime) { + kotlinx.coroutines.delay(3000) + isIdle = true + } + + // 菜单栏透明度动画 + val navBarAlpha by animateFloatAsState( + targetValue = if (isIdle && !isDragging) 0.4f else 1f, + animationSpec = tween(durationMillis = 500, easing = androidx.compose.animation.core.EaseInOut), + label = "nav_bar_alpha" + ) + // 当外部 selectedNavItem 变化时,同步更新 dragIndex LaunchedEffect(selectedNavItem) { if (!isDragging) { @@ -616,13 +730,14 @@ fun FloatingBottomNav( Box( modifier = modifier .height(navBarHeight) + .alpha(navBarAlpha) .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) + Color.White.copy(alpha = 0.95f), + Color(0xFFF5F5F5).copy(alpha = 0.95f) ) ) ) @@ -630,8 +745,8 @@ fun FloatingBottomNav( width = 1.dp, brush = Brush.linearGradient( colors = listOf( - Color.White.copy(alpha = 0.1f), - Color.White.copy(alpha = 0.05f) + Color.White.copy(alpha = 0.5f), + Color(0xFFE0E0E0).copy(alpha = 0.3f) ) ), shape = RoundedCornerShape(50) @@ -640,6 +755,9 @@ fun FloatingBottomNav( .pointerInput(Unit) { detectHorizontalDragGestures( onDragStart = { offset -> + // 重置空闲状态 + isIdle = false + lastInteractionTime = System.currentTimeMillis() isDragging = true val index = (offset.x / (itemWidth.toPx() + itemSpacing.toPx())).toInt() .coerceIn(0, navItems.size - 1) @@ -647,10 +765,12 @@ fun FloatingBottomNav( }, onDragEnd = { isDragging = false + lastInteractionTime = System.currentTimeMillis() onNavItemSelect(dragIndex) }, onDragCancel = { isDragging = false + lastInteractionTime = System.currentTimeMillis() dragIndex = selectedNavItem }, onHorizontalDrag = { change, _ -> @@ -676,10 +796,14 @@ fun FloatingBottomNav( .offset(x = animatedOffset.dp + horizontalOffset.dp) .size(indicatorSize.dp) .clip(RoundedCornerShape(50)) - .background(Color.White.copy(alpha = if (isDragging) 0.2f else 0.12f)) + .background( + Brush.linearGradient( + colors = listOf(Color(0xFF00D1FF), Color(0xFFB89CFF)) + ) + ) .border( width = if (isDragging) 2.dp else 1.5.dp, - color = Color.White.copy(alpha = if (isDragging) 0.6f else 0.25f), + color = Color.White.copy(alpha = if (isDragging) 0.8f else 0.5f), shape = RoundedCornerShape(50) ) ) @@ -701,6 +825,9 @@ fun FloatingBottomNav( interactionSource = remember { MutableInteractionSource() } ) { if (!isDragging) { + // 重置空闲状态 + isIdle = false + lastInteractionTime = System.currentTimeMillis() onNavItemSelect(index) } }, @@ -711,7 +838,7 @@ fun FloatingBottomNav( contentDescription = null, modifier = Modifier.size(24.dp), colorFilter = androidx.compose.ui.graphics.ColorFilter.tint( - if (isCurrentSelected) Color.White else Color(0xFFB0B0B0) + if (isCurrentSelected) Color.White else Color(0xFF666666) ) ) } @@ -869,6 +996,34 @@ fun TopBar() { // 获取用户信息 val userInfo by com.example.smarthome.data.UserManager.userInfo.collectAsState() + // 当前时间 + var currentTime by remember { mutableStateOf(System.currentTimeMillis()) } + LaunchedEffect(Unit) { + while (true) { + currentTime = System.currentTimeMillis() + kotlinx.coroutines.delay(1000) + } + } + + // 格式化日期时间 + val dateFormat = remember { java.text.SimpleDateFormat("yyyy年M月d日 EEEE", java.util.Locale.CHINESE) } + val timeFormat = remember { java.text.SimpleDateFormat("HH:mm", java.util.Locale.getDefault()) } + val dateString = remember(currentTime) { dateFormat.format(java.util.Date(currentTime)) } + val timeString = remember(currentTime) { timeFormat.format(java.util.Date(currentTime)) } + + // 根据时间生成问候语 + val calendar = remember(currentTime) { java.util.Calendar.getInstance().apply { timeInMillis = currentTime } } + val hour = calendar.get(java.util.Calendar.HOUR_OF_DAY) + val greeting = when { + hour < 6 -> "夜深了" + hour < 9 -> "早上好" + hour < 12 -> "上午好" + hour < 14 -> "中午好" + hour < 18 -> "下午好" + hour < 22 -> "晚上好" + else -> "夜深了" + } + LaunchedEffect(Unit) { try { weatherInfo = WeatherService.getWeather(context) @@ -879,7 +1034,40 @@ fun TopBar() { } Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) { - Text(text = tr("dashboard_title"), style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = Color.White) + // 左侧:欢迎语和日期时间 + Column { + Row(verticalAlignment = Alignment.Bottom) { + Text( + text = "$greeting,${userInfo.nickname}", + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.Bold, + color = Color.White + ) + } + Spacer(modifier = Modifier.height(4.dp)) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = timeString, + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = Color.White.copy(alpha = 0.9f) + ) + Box( + modifier = Modifier + .size(4.dp) + .clip(androidx.compose.foundation.shape.CircleShape) + .background(Color.White.copy(alpha = 0.5f)) + ) + Text( + text = dateString, + fontSize = 14.sp, + color = Color.White.copy(alpha = 0.7f) + ) + } + } Row( horizontalArrangement = Arrangement.spacedBy(12.dp), @@ -910,16 +1098,10 @@ fun UserAvatar( .size(40.dp) .shadow(8.dp, androidx.compose.foundation.shape.CircleShape) .clip(androidx.compose.foundation.shape.CircleShape) - .background( - Brush.linearGradient( - listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF)) - ) - ) + .background(Color.White.copy(alpha = 0.95f)) .border( width = 2.dp, - brush = Brush.linearGradient( - listOf(Color.White.copy(alpha = 0.5f), Color.White.copy(alpha = 0.2f)) - ), + color = Color.White.copy(alpha = 0.5f), shape = androidx.compose.foundation.shape.CircleShape ) .clickable( @@ -935,7 +1117,7 @@ fun UserAvatar( text = nickname.firstOrNull()?.uppercase() ?: "U", fontSize = 16.sp, fontWeight = FontWeight.Bold, - color = Color.White + color = Color(0xFF1A1A1A) ) } else { // 显示昵称首字母 @@ -943,7 +1125,7 @@ fun UserAvatar( text = nickname.firstOrNull()?.uppercase() ?: "U", fontSize = 16.sp, fontWeight = FontWeight.Bold, - color = Color.White + color = Color(0xFF1A1A1A) ) } } @@ -951,191 +1133,144 @@ fun UserAvatar( @Composable fun WeatherCard(weatherInfo: WeatherInfo, isLoading: Boolean) { - // 动画:创建无限循环的偏移量 - val infiniteTransition = rememberInfiniteTransition(label = "weather") - val offsetAnim by infiniteTransition.animateFloat( - initialValue = 0f, - targetValue = 1500f, // 增加偏移量,让流动范围更大 - animationSpec = infiniteRepeatable( - animation = tween(durationMillis = 15000, easing = LinearEasing), // 加快一点速度 - repeatMode = RepeatMode.Reverse - ), - label = "gradient" - ) - - // 根据天气状况决定背景颜色 - val colors = if (isLoading) { - listOf( - Color(0xFF2C3E50).copy(alpha = 0.6f), - Color(0xFF3498DB).copy(alpha = 0.6f), - Color(0xFF2C3E50).copy(alpha = 0.6f) - ) - } else { - when { - // 晴天:深橙色流动 - weatherInfo.weather.contains("晴") -> - listOf( - Color(0xFF8B3000).copy(alpha = 0.65f), - Color(0xFF9A4500).copy(alpha = 0.65f), - Color(0xFF8B3000).copy(alpha = 0.65f) - ) - // 雨天:深紫深蓝流动 - weatherInfo.weather.contains("雨") -> - listOf( - Color(0xFF2C3E50).copy(alpha = 0.6f), - Color(0xFF4CA1AF).copy(alpha = 0.6f), - Color(0xFF2C3E50).copy(alpha = 0.6f) - ) - // 阴天/多云:灰蓝流动 - weatherInfo.weather.contains("云") || weatherInfo.weather.contains("阴") -> - listOf( - Color(0xFF606c88).copy(alpha = 0.6f), - Color(0xFF3f4c6b).copy(alpha = 0.6f), - Color(0xFF606c88).copy(alpha = 0.6f) - ) - // 雪天:冰蓝流动 - weatherInfo.weather.contains("雪") -> - listOf( - Color(0xFF5a7fa4).copy(alpha = 0.6f), - Color(0xFF8ad0d4).copy(alpha = 0.6f), - Color(0xFF5a7fa4).copy(alpha = 0.6f) - ) - // 默认:蓝青流动 - else -> - listOf( - Color(0xFF1090B0).copy(alpha = 0.6f), - Color(0xFF000046).copy(alpha = 0.6f), - Color(0xFF1090B0).copy(alpha = 0.6f) - ) - } + // 根据天气确定主题色 + val weatherColors = when { + weatherInfo.weather.contains("晴") -> Pair(Color(0xFFFBBF24), Color(0xFFEA580C)) // amber to orange + weatherInfo.weather.contains("雨") -> Pair(Color(0xFF60A5FA), Color(0xFF3B82F6)) // blue + weatherInfo.weather.contains("云") || weatherInfo.weather.contains("阴") -> Pair(Color(0xFF9CA3AF), Color(0xFF6B7280)) // gray + weatherInfo.weather.contains("雪") -> Pair(Color(0xFFE0F2FE), Color(0xFF7DD3FC)) // light blue + weatherInfo.weather.contains("雾") || weatherInfo.weather.contains("霾") -> Pair(Color(0xFF94A3B8), Color(0xFF64748B)) // slate gray + else -> Pair(Color(0xFF38BDF8), Color(0xFF0EA5E9)) // sky blue } - - val backgroundBrush = Brush.linearGradient( - colors = colors, - start = Offset(offsetAnim, offsetAnim), - end = Offset(offsetAnim + 500f, offsetAnim + 500f) - ) + val accentColor = weatherColors.first - Box( + Row( modifier = Modifier .clip(RoundedCornerShape(16.dp)) - .background(backgroundBrush) // 使用动态渐变背景 + .background(accentColor.copy(alpha = 0.15f)) + .border( + width = 1.dp, + color = accentColor.copy(alpha = 0.2f), + shape = RoundedCornerShape(16.dp) + ) + .padding(start = 4.dp, top = 4.dp, bottom = 4.dp, end = 20.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically ) { - // 天气特效层(在背景之上,内容之下)- 使用固定大小 - WeatherEffectLayer( - weather = weatherInfo.weather, - modifier = Modifier.size(width = 300.dp, height = 60.dp) - ) - - Row( - modifier = Modifier.padding(horizontal = 20.dp, vertical = 12.dp), - horizontalArrangement = Arrangement.spacedBy(20.dp), - verticalAlignment = Alignment.CenterVertically + // 天气图标(带阴影的渐变背景) + Box( + modifier = Modifier + .size(48.dp) + .shadow( + elevation = 8.dp, + shape = RoundedCornerShape(14.dp), + spotColor = weatherColors.second.copy(alpha = 0.3f) + ) + .clip(RoundedCornerShape(14.dp)) + .background( + Brush.linearGradient( + colors = listOf(weatherColors.first, weatherColors.second), + start = Offset(0f, 0f), + end = Offset(100f, 100f) + ) + ), + contentAlignment = Alignment.Center ) { - // 温度和天气 - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text( - text = if (isLoading) "--" else "${weatherInfo.temperature}°C", - fontSize = 22.sp, - fontWeight = FontWeight.Bold, - color = Color.White, - style = androidx.compose.ui.text.TextStyle( - shadow = Shadow(color = Color.Black.copy(alpha = 0.3f), blurRadius = 4f) - ) - ) - Text( - text = if (isLoading) "加载中" else weatherInfo.weather, - fontSize = 14.sp, - fontWeight = FontWeight.Medium, - color = Color.White, - style = androidx.compose.ui.text.TextStyle( - shadow = Shadow(color = Color.Black.copy(alpha = 0.3f), blurRadius = 4f) - ) - ) - } - - // 分隔线 - Box( - modifier = Modifier - .width(1.dp) - .height(36.dp) - .background(Color.White.copy(alpha = 0.3f)) + Image( + painter = painterResource(id = when { + weatherInfo.weather.contains("晴") -> R.drawable.ic_weather_sunny + weatherInfo.weather.contains("雨") -> R.drawable.ic_weather_rainy + weatherInfo.weather.contains("云") || weatherInfo.weather.contains("阴") -> R.drawable.ic_weather_cloudy + weatherInfo.weather.contains("雪") -> R.drawable.ic_weather_snowy + weatherInfo.weather.contains("雾") || weatherInfo.weather.contains("霾") -> R.drawable.ic_weather_foggy + else -> R.drawable.ic_weather_cloudy + }), + contentDescription = "天气", + modifier = Modifier.size(24.dp), + colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(Color.White) ) - - // 湿度 - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text( - text = if (isLoading) "--" else "${weatherInfo.humidity}%", - fontSize = 18.sp, - fontWeight = FontWeight.SemiBold, - color = Color.White, - style = androidx.compose.ui.text.TextStyle( - shadow = Shadow(color = Color.Black.copy(alpha = 0.3f), blurRadius = 4f) - ) - ) - Text( - text = "湿度", - fontSize = 13.sp, - color = Color.White.copy(alpha = 0.8f), - style = androidx.compose.ui.text.TextStyle( - shadow = Shadow(color = Color.Black.copy(alpha = 0.3f), blurRadius = 4f) - ) - ) - } - - // 分隔线 - Box( - modifier = Modifier - .width(1.dp) - .height(36.dp) - .background(Color.White.copy(alpha = 0.3f)) - ) - - // 空气质量 - Column(horizontalAlignment = Alignment.CenterHorizontally) { - val aqiColor = when { - weatherInfo.aqi <= 50 -> Color(0xFF4CAF50) - weatherInfo.aqi <= 100 -> Color(0xFFFFEB3B) - weatherInfo.aqi <= 150 -> Color(0xFFFF9800) - else -> Color(0xFFFF5252) - } - Text( - text = if (isLoading) "--" else weatherInfo.airQuality, - fontSize = 18.sp, - fontWeight = FontWeight.SemiBold, - color = aqiColor, - style = androidx.compose.ui.text.TextStyle( - shadow = Shadow(color = Color.Black.copy(alpha = 0.3f), blurRadius = 4f) - ) - ) - Text( - text = if (isLoading) "AQI" else "AQI ${weatherInfo.aqi}", - fontSize = 13.sp, - color = Color.White.copy(alpha = 0.8f), - style = androidx.compose.ui.text.TextStyle( - shadow = Shadow(color = Color.Black.copy(alpha = 0.3f), blurRadius = 4f) - ) - ) - } - - // 城市 + } + + // 温度和天气状况 + Column { Text( - text = weatherInfo.city, - fontSize = 15.sp, - fontWeight = FontWeight.Medium, + text = if (isLoading) "--°C" else "${weatherInfo.temperature}°C", + fontSize = 22.sp, + fontWeight = FontWeight.Bold, color = Color.White, - style = androidx.compose.ui.text.TextStyle( - shadow = Shadow(color = Color.Black.copy(alpha = 0.3f), blurRadius = 4f) - ) + lineHeight = 22.sp ) + Text( + text = if (isLoading) "加载中" else weatherInfo.weather, + fontSize = 11.sp, + fontWeight = FontWeight.Medium, + color = accentColor.copy(alpha = 0.8f) + ) + } + + // 分隔线 + Box( + modifier = Modifier + .width(1.dp) + .height(32.dp) + .background(Color.White.copy(alpha = 0.1f)) + ) + + // 湿度和AQI + Column( + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + // 湿度 + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp) + ) { + Image( + painter = painterResource(id = R.drawable.ic_humidity), + contentDescription = "湿度", + modifier = Modifier.size(12.dp), + colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(Color(0xFFD1D5DB)) + ) + Text( + text = if (isLoading) "--%" else "${weatherInfo.humidity}%", + fontSize = 11.sp, + color = Color(0xFFD1D5DB) + ) + } + + // AQI + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp) + ) { + Image( + painter = painterResource(id = R.drawable.ic_wind), + contentDescription = "AQI", + modifier = Modifier.size(12.dp), + colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(Color(0xFFD1D5DB)) + ) + Text( + text = if (isLoading) "AQI --" else "AQI ${weatherInfo.aqi}", + fontSize = 11.sp, + color = Color(0xFFD1D5DB) + ) + } } } } @Composable fun GradientButton(text: String, onClick: () -> Unit = {}) { - Box(modifier = Modifier.height(36.dp).clip(RoundedCornerShape(20.dp)).background(Brush.linearGradient(listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF)))).clickable(indication = null, interactionSource = remember { MutableInteractionSource() }) { onClick() }.padding(horizontal = 16.dp), contentAlignment = Alignment.Center) { - Text(text = text, color = Color.Black, fontWeight = FontWeight.Medium) + Box( + modifier = Modifier + .height(36.dp) + .clip(RoundedCornerShape(20.dp)) + .background(Color.White.copy(alpha = 0.95f)) + .clickable(indication = null, interactionSource = remember { MutableInteractionSource() }) { onClick() } + .padding(horizontal = 16.dp), + contentAlignment = Alignment.Center + ) { + Text(text = text, color = Color(0xFF1A1A1A), fontWeight = FontWeight.SemiBold) } } @@ -1229,15 +1364,6 @@ fun UsageStatusChart(modifier: Modifier = Modifier, roomName: String = "房间") roomEnergyData[roomName] ?: RoomEnergy(2.0, 5, 4, "↓", 8, 1.2, 200) } - val values = remember(roomName) { - if (isOverview) { - // 总览:生成更高的数值 - List(12) { (40..90).random().toFloat() } - } else { - List(12) { (15..45).random().toFloat() } - } - } - val maxValue = remember(values) { values.maxOrNull() ?: 45f } val totalPower = String.format("%.1f", roomData.power) val totalHours = roomData.hours val activeDevices = roomData.devices @@ -1251,19 +1377,17 @@ fun UsageStatusChart(modifier: Modifier = Modifier, roomName: String = "房间") Box( modifier = modifier - .height(260.dp) .clip(RoundedCornerShape(24.dp)) - .background(Color(0xFF1E2530).copy(alpha = 0.85f)) + .background(Color(0xFF1A1A1A).copy(alpha = 0.65f)) .border( - width = 1.dp, - color = Color.White.copy(alpha = 0.15f), + width = 2.dp, + color = Color.White.copy(alpha = 0.25f), shape = RoundedCornerShape(24.dp) ) .padding(16.dp) ) { Column( - verticalArrangement = Arrangement.spacedBy(12.dp), - modifier = Modifier.fillMaxSize() + verticalArrangement = Arrangement.spacedBy(12.dp) ) { // 标题区域 Row( @@ -1364,10 +1488,12 @@ fun UsageStatusChart(modifier: Modifier = Modifier, roomName: String = "房间") Row( modifier = Modifier .fillMaxWidth() + .height(56.dp) .clip(RoundedCornerShape(12.dp)) - .background(Color(0xFF2A2A3E).copy(alpha = 0.5f)) - .padding(12.dp), - horizontalArrangement = Arrangement.SpaceEvenly + .background(Color(0xFF2A2A2A).copy(alpha = 0.4f)) + .padding(horizontal = 12.dp), + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically ) { // 运行时长 Column(horizontalAlignment = Alignment.CenterHorizontally) { @@ -1385,7 +1511,7 @@ fun UsageStatusChart(modifier: Modifier = Modifier, roomName: String = "房间") modifier = Modifier .width(1.dp) .height(30.dp) - .background(Color(0xFF3A3A4E)) + .background(Color(0xFF3A3A3A).copy(alpha = 0.5f)) ) // 活跃设备 @@ -1404,7 +1530,7 @@ fun UsageStatusChart(modifier: Modifier = Modifier, roomName: String = "房间") modifier = Modifier .width(1.dp) .height(30.dp) - .background(Color(0xFF3A3A4E)) + .background(Color(0xFF3A3A3A).copy(alpha = 0.5f)) ) // 峰值功率 @@ -1418,44 +1544,6 @@ fun UsageStatusChart(modifier: Modifier = Modifier, roomName: String = "房间") Text(text = "W峰值", fontSize = 10.sp, color = Color(0xFF9AA0A6)) } } - - // 图表区域 - Box( - modifier = Modifier - .fillMaxWidth() - .weight(1f) - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .align(Alignment.BottomCenter), - horizontalArrangement = Arrangement.spacedBy(4.dp), - verticalAlignment = Alignment.Bottom - ) { - values.forEachIndexed { index, v -> - val normalizedHeight = (v / maxValue) * 50 - val isHighlight = index == values.size - 1 - - Box( - modifier = Modifier - .weight(1f) - .height(normalizedHeight.dp) - .clip(RoundedCornerShape(topStart = 3.dp, topEnd = 3.dp)) - .background( - if (isHighlight) { - Brush.verticalGradient( - colors = listOf(accentColor, accentColor.copy(alpha = 0.5f)) - ) - } else { - Brush.verticalGradient( - colors = listOf(Color(0xFF4A4A5A), Color(0xFF3A3A4A)) - ) - } - ) - ) - } - } - } } } } @@ -1540,12 +1628,11 @@ fun AirConditionerCard(modifier: Modifier = Modifier, roomName: String = "房间 Box( modifier = modifier - .height(260.dp) .clip(RoundedCornerShape(24.dp)) - .background(Color(0xFF1E2530).copy(alpha = 0.85f)) + .background(Color(0xFF1A1A1A).copy(alpha = 0.65f)) .border( - width = 1.dp, - color = Color.White.copy(alpha = 0.15f), + width = 2.dp, + color = Color.White.copy(alpha = 0.25f), shape = RoundedCornerShape(24.dp) ) .padding(16.dp) @@ -1627,7 +1714,7 @@ fun AirConditionerCard(modifier: Modifier = Modifier, roomName: String = "房间 horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier .clip(RoundedCornerShape(12.dp)) - .background(if (isOn) accentColor.copy(alpha = 0.2f) else Color(0xFF3A3A4E)) + .background(if (isOn) accentColor.copy(alpha = 0.2f) else Color(0xFF2A2A2A).copy(alpha = 0.4f)) .clickable(enabled = isOn) { fanSpeed = (fanSpeed + 1) % 4 } .padding(horizontal = 12.dp, vertical = 8.dp) ) { @@ -1660,7 +1747,7 @@ fun AirConditionerCard(modifier: Modifier = Modifier, roomName: String = "房间 modifier = Modifier .size(36.dp) .clip(RoundedCornerShape(10.dp)) - .background(if (isOn) accentColor.copy(alpha = 0.3f) else Color(0xFF3A3A4E)) + .background(if (isOn) accentColor.copy(alpha = 0.3f) else Color(0xFF2A2A2A).copy(alpha = 0.4f)) .clickable(enabled = isOn && temp < 32f) { temp += 1f }, contentAlignment = Alignment.Center ) { @@ -1671,7 +1758,7 @@ fun AirConditionerCard(modifier: Modifier = Modifier, roomName: String = "房间 modifier = Modifier .size(36.dp) .clip(RoundedCornerShape(10.dp)) - .background(if (isOn) accentColor.copy(alpha = 0.3f) else Color(0xFF3A3A4E)) + .background(if (isOn) accentColor.copy(alpha = 0.3f) else Color(0xFF2A2A2A).copy(alpha = 0.4f)) .clickable(enabled = isOn && temp > 16f) { temp -= 1f }, contentAlignment = Alignment.Center ) { @@ -1697,14 +1784,13 @@ fun AirConditionerCard(modifier: Modifier = Modifier, roomName: String = "房间 Box( modifier = Modifier .weight(1f) - .heightIn(min = 44.dp) + .height(56.dp) .clip(RoundedCornerShape(12.dp)) .background( if (isSelected && isOn) modeColor.copy(alpha = 0.4f) - else Color(0xFF2A2A3E).copy(alpha = 0.6f) + else Color(0xFF2A2A2A).copy(alpha = 0.4f) ) - .clickable(enabled = isOn) { selectedMode = index } - .padding(vertical = 4.dp), + .clickable(enabled = isOn) { selectedMode = index }, contentAlignment = Alignment.Center ) { Column(horizontalAlignment = Alignment.CenterHorizontally) { @@ -1884,15 +1970,12 @@ fun DotsProgress(percent: Float, onPercentChange: (Float) -> Unit = {}, enabled: } } -enum class Mode { HOME, AWAY, FUN } +enum class Mode { HOME, AWAY, FUN, MOVIE } @Composable fun ModeButtonsRow(onModeSelected: (Mode) -> Unit) { BoxWithConstraints(modifier = Modifier.fillMaxWidth()) { - // 固定高度,确保在滚动前完全显示 - val buttonHeight = 100.dp - - var selected by remember { mutableStateOf(null) } + var selected by remember { mutableStateOf(Mode.HOME) } val isVertical = maxWidth < 480.dp val containerModifier = Modifier.fillMaxWidth() @@ -1902,6 +1985,7 @@ fun ModeButtonsRow(onModeSelected: (Mode) -> Unit) { Triple(Mode.HOME, Brush.linearGradient(listOf(Color(0xFF00D1FF), Color(0xFF00C9A7))), "回家"), Triple(Mode.AWAY, Brush.linearGradient(listOf(Color(0xFFFF8A65), Color(0xFFFF7043))), "出门"), Triple(Mode.FUN, Brush.linearGradient(listOf(Color(0xFF6A3DFF), Color(0xFF3A0CA3))), "玩乐"), + Triple(Mode.MOVIE, Brush.linearGradient(listOf(Color(0xFF1A237E), Color(0xFF4A148C))), "观影"), ) if (isVertical) { @@ -1909,8 +1993,7 @@ fun ModeButtonsRow(onModeSelected: (Mode) -> Unit) { items.forEach { (mode, brush, label) -> ModeButton( text = label, - modifier = Modifier.fillMaxWidth(), - height = buttonHeight, + modifier = Modifier.fillMaxWidth().aspectRatio(1.3f), brush = brush, selected = selected == mode, onClick = { @@ -1925,8 +2008,7 @@ fun ModeButtonsRow(onModeSelected: (Mode) -> Unit) { items.forEach { (mode, brush, label) -> ModeButton( text = label, - modifier = Modifier.weight(1f), - height = buttonHeight, + modifier = Modifier.weight(1f).aspectRatio(1.3f), brush = brush, selected = selected == mode, onClick = { @@ -1944,7 +2026,6 @@ fun ModeButtonsRow(onModeSelected: (Mode) -> Unit) { fun ModeButton( text: String, modifier: Modifier, - height: androidx.compose.ui.unit.Dp, brush: Brush, selected: Boolean, onClick: () -> Unit @@ -1958,6 +2039,7 @@ fun ModeButton( "回家" -> R.drawable.ic_mode_home "出门" -> R.drawable.ic_mode_away "玩乐" -> R.drawable.ic_mode_fun + "观影" -> R.drawable.ic_media else -> R.drawable.ic_dashboard } @@ -1997,30 +2079,24 @@ fun ModeButton( Box( modifier = modifier - .height(height) .scale(scale) .clip(shape) .then( if (selected) { Modifier .shadow(elevation, shape, clip = false) - .background(brush, shape) + .background(Color.White, shape) .border( - width = 2.dp, - brush = Brush.linearGradient( - listOf( - Color(0x99FFFFFF), - Color(0x66FFFFFF) - ) - ), + width = 1.dp, + color = Color(0xFFE0E0E0), shape = shape ) } else { Modifier - .background(Color(0xFF1E2530).copy(alpha = 0.85f)) + .background(Color(0xFF1A1A1A).copy(alpha = 0.65f)) .border( - width = 1.dp, - color = Color.White.copy(alpha = 0.15f), + width = 2.dp, + color = Color.White.copy(alpha = 0.25f), shape = shape ) } @@ -2029,98 +2105,29 @@ fun ModeButton( view.playSoundEffect(SoundEffectConstants.CLICK) onClick() } - .padding(24.dp), + .padding(16.dp), contentAlignment = Alignment.Center ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - // 图标容器 + // 右上角绿色小点(仅选中时显示) + if (selected) { Box( modifier = Modifier - .size(if (selected) 72.dp else 64.dp) - .scale(iconScale), - contentAlignment = Alignment.Center - ) { - // 外层光晕效果(仅选中时) - if (selected) { - Box( - modifier = Modifier - .fillMaxSize() - .clip(RoundedCornerShape(20.dp)) - .background( - Brush.radialGradient( - colors = listOf( - Color.White.copy(alpha = 0.2f), - Color.Transparent - ), - radius = 100f - ) - ) - ) - } - - // 图标背景 - Box( - modifier = Modifier - .size(if (selected) 64.dp else 56.dp) - .clip(RoundedCornerShape(18.dp)) - .background( - if (selected) { - Brush.linearGradient( - colors = listOf( - Color.White.copy(alpha = 0.25f), - Color.White.copy(alpha = 0.15f) - ) - ) - } else { - Brush.linearGradient( - colors = listOf( - Color(0x55FFFFFF), - Color(0x55FFFFFF) - ) - ) - } - ) - .then( - if (selected) { - Modifier.border( - width = 1.dp, - brush = Brush.linearGradient( - colors = listOf( - Color.White.copy(alpha = 0.4f), - Color.White.copy(alpha = 0.1f) - ) - ), - shape = RoundedCornerShape(18.dp) - ) - } else { - Modifier - } - ), - contentAlignment = Alignment.Center - ) { - Image( - painter = painterResource(id = iconRes), - contentDescription = text, - modifier = Modifier.size(if (selected) 38.dp else 34.dp), - colorFilter = androidx.compose.ui.graphics.ColorFilter.tint( - Color.White - ) - ) - } - } - - // 文字 - Text( - text = text, - color = Color.White, - fontWeight = if (selected) FontWeight.Bold else FontWeight.Medium, - fontSize = if (selected) 20.sp else 18.sp, - style = MaterialTheme.typography.titleMedium + .align(Alignment.TopEnd) + .size(10.dp) + .clip(RoundedCornerShape(5.dp)) + .background(Color(0xFF4CAF50)) ) } + + // 图标 + Image( + painter = painterResource(id = iconRes), + contentDescription = text, + modifier = Modifier.size(if (selected) 40.dp else 36.dp), + colorFilter = androidx.compose.ui.graphics.ColorFilter.tint( + if (selected) Color(0xFF1A1A1A) else Color.White + ) + ) } } @@ -2143,81 +2150,179 @@ fun LightRow(modifier: Modifier = Modifier, roomName: String = "房间") { @Composable fun LightCard(name: String, percent: Float, modifier: Modifier = Modifier) { var brightness by remember { mutableStateOf(percent) } + var isOn by remember { mutableStateOf(percent > 0.05f) } - Box( + Column( modifier = modifier - .height(72.dp) - .clip(RoundedCornerShape(16.dp)) - .background(Color(0xFF1E2530).copy(alpha = 0.85f)) + .clip(RoundedCornerShape(20.dp)) + .background( + if (isOn) { + Color(0xFF2A2010).copy(alpha = 0.7f) + } else { + Color(0xFF1A1A1A).copy(alpha = 0.65f) + } + ) .border( width = 1.dp, - color = Color.White.copy(alpha = 0.15f), - shape = RoundedCornerShape(16.dp) + color = if (isOn) { + Color(0xFFFFB74D).copy(alpha = 0.4f) + } else { + Color.White.copy(alpha = 0.15f) + }, + shape = RoundedCornerShape(20.dp) ) - .padding(horizontal = 12.dp, vertical = 8.dp) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) ) { - Row(horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { - Row(horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically) { - // 灯光图标 - 带发光效果 - Box( - modifier = Modifier - .size(40.dp) - .shadow( - elevation = if (brightness > 0.3f) (brightness * 12).dp else 0.dp, - shape = RoundedCornerShape(12.dp), - spotColor = Color(0xFFFFD54F).copy(alpha = brightness * 0.6f) - ) - .clip(RoundedCornerShape(12.dp)) - .background( - if (brightness > 0.1f) { - Brush.radialGradient( - colors = listOf( - Color(0xFFFFE082).copy(alpha = brightness), - Color(0xFFFFD54F).copy(alpha = brightness * 0.9f), - Color(0xFFFF8F00).copy(alpha = brightness * 0.7f) - ), - radius = 60f + // 顶部:图标和文字 + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // 灯泡图标(点击切换开关) + Box( + modifier = Modifier + .size(44.dp) + .clip(RoundedCornerShape(12.dp)) + .background( + if (isOn) { + Brush.linearGradient( + colors = listOf( + Color(0xFFFFB74D), + Color(0xFFFFA726) ) - } else { - Brush.linearGradient( - listOf( - Color(0xFF3A3A3A), - Color(0xFF2A2A2A) - ) + ) + } else { + Brush.linearGradient( + colors = listOf( + Color(0xFF2A2A2A), + Color(0xFF1F1F1F) ) + ) + } + ) + .then( + if (!isOn) { + Modifier.border( + width = 1.dp, + color = Color.White.copy(alpha = 0.1f), + shape = RoundedCornerShape(12.dp) + ) + } else Modifier + ) + .clickable { isOn = !isOn }, + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource(id = R.drawable.ic_light), + contentDescription = "灯光", + modifier = Modifier.size(24.dp), + colorFilter = androidx.compose.ui.graphics.ColorFilter.tint( + if (isOn) Color.White else Color(0xFF666666) + ) + ) + } + + // 名称和状态 + Column { + Text( + text = name, + color = Color.White, + fontWeight = FontWeight.Medium, + fontSize = 15.sp + ) + Text( + text = if (isOn) "${(brightness * 100).toInt()}% 亮度" else "已关闭", + color = Color(0xFF9AA0A6), + fontSize = 13.sp + ) + } + } + + // 底部:亮度滑块条 + LightSlider( + brightness = brightness, + onBrightnessChange = { brightness = it }, + enabled = isOn + ) + } +} + +@Composable +fun LightSlider( + brightness: Float, + onBrightnessChange: (Float) -> Unit, + enabled: Boolean = true +) { + val totalDots = 10 + val filledDots = if (enabled) (brightness * totalDots).toInt() else 0 + + Box( + modifier = Modifier + .fillMaxWidth() + .height(32.dp) + .clip(RoundedCornerShape(16.dp)) + .background(Color(0xFF2A2A2A).copy(alpha = if (enabled) 1f else 0.5f)) + .then( + if (enabled) { + Modifier + .pointerInput(Unit) { + detectTapGestures { offset -> + val newBrightness = (offset.x / size.width).coerceIn(0f, 1f) + onBrightnessChange(newBrightness) } - ), - contentAlignment = Alignment.Center - ) { - Image( - painter = painterResource(id = R.drawable.ic_light), - contentDescription = "灯光", - modifier = Modifier.size(24.dp), - colorFilter = androidx.compose.ui.graphics.ColorFilter.tint( - if (brightness > 0.1f) { - Color.White.copy(alpha = 0.95f) - } else { - Color(0xFF666666) + } + .pointerInput(Unit) { + detectHorizontalDragGestures { change, _ -> + change.consume() + val newBrightness = (change.position.x / size.width).coerceIn(0f, 1f) + onBrightnessChange(newBrightness) } + } + } else Modifier + ) + ) { + // 填充部分(橙色/棕色渐变)- 关闭时不显示 + if (enabled) { + Box( + modifier = Modifier + .fillMaxHeight() + .fillMaxWidth(brightness.coerceIn(0f, 1f)) + .clip(RoundedCornerShape(16.dp)) + .background( + Brush.horizontalGradient( + colors = listOf( + Color(0xFFD4A574), + Color(0xFFB8956E) + ) ) ) - } - Column { - Text(text = name, color = Color.White, fontWeight = FontWeight.Medium) - Text( - text = "${(brightness * 100).toInt()}%", - color = if (brightness > 0.1f) Color(0xFFFFD54F) else Color(0xFF9AA0A6), - fontSize = 13.sp, - fontWeight = if (brightness > 0.5f) FontWeight.Medium else FontWeight.Normal - ) - } - } - DotsProgress( - percent = brightness, - onPercentChange = { brightness = it }, - enabled = true ) } + + // 点点指示器 + Row( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 12.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + repeat(totalDots) { index -> + Box( + modifier = Modifier + .size(6.dp) + .clip(RoundedCornerShape(3.dp)) + .background( + if (enabled && index < filledDots) { + Color(0xFF8B6914) + } else { + Color(0xFF5A5A5A).copy(alpha = if (enabled) 1f else 0.5f) + } + ) + ) + } + } } } @@ -2861,7 +2966,8 @@ fun EditRoomsDialog( rooms: List, onDismiss: () -> Unit, onRenameRoom: (Int, String) -> Unit, - onDeleteRoom: (Int) -> Unit + onDeleteRoom: (Int) -> Unit, + onAddRoom: () -> Unit = {} ) { // 当前编辑的房间索引 var editingIndex by remember { mutableStateOf(-1) } @@ -2875,26 +2981,19 @@ fun EditRoomsDialog( indication = null, interactionSource = remember { MutableInteractionSource() } ) { onDismiss() }, - contentAlignment = Alignment.Center + contentAlignment = Alignment.TopCenter ) { - // 对话框主体 + // 对话框主体 - 往上挪,使用透明卡片风格 Box( modifier = Modifier - .width(420.dp) + .padding(top = 60.dp, bottom = 60.dp) + .widthIn(max = 420.dp) + .fillMaxWidth(0.9f) .clip(RoundedCornerShape(28.dp)) - .background( - Brush.verticalGradient( - listOf( - Color(0xFF2A2A3E), - Color(0xFF1A1A2E) - ) - ) - ) + .background(Color(0xFF1A1A1A).copy(alpha = 0.65f)) .border( width = 1.dp, - brush = Brush.linearGradient( - listOf(Color(0xFF00D1FF).copy(alpha = 0.3f), Color(0xFFB89CFF).copy(alpha = 0.3f)) - ), + color = Color.White.copy(alpha = 0.15f), shape = RoundedCornerShape(28.dp) ) .clickable( @@ -2956,41 +3055,37 @@ fun EditRoomsDialog( } } - // 房间列表 + // 房间列表 - 使用 weight 占据剩余空间,确保底部按钮始终可见 Column( verticalArrangement = Arrangement.spacedBy(8.dp), - modifier = Modifier.heightIn(max = 300.dp).verticalScroll(rememberScrollState()) + modifier = Modifier + .weight(1f, fill = false) + .verticalScroll(rememberScrollState()) ) { rooms.forEachIndexed { index, name -> Row( modifier = Modifier .fillMaxWidth() .clip(RoundedCornerShape(14.dp)) - .background(Color(0xFF1A1A2E)) + .background(Color.White.copy(alpha = 0.1f)) .padding(12.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { if (editingIndex == index) { - // 编辑模式 - TextField( + // 编辑模式 - 使用 BasicTextField 去掉内层框 + BasicTextField( 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 + .padding(end = 12.dp), + textStyle = androidx.compose.ui.text.TextStyle( + fontSize = 15.sp, + color = Color.White ), - shape = RoundedCornerShape(10.dp), singleLine = true, - textStyle = androidx.compose.ui.text.TextStyle(fontSize = 15.sp) + cursorBrush = SolidColor(Color(0xFF00D1FF)) ) Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { @@ -3037,7 +3132,7 @@ fun EditRoomsDialog( modifier = Modifier .size(36.dp) .clip(RoundedCornerShape(8.dp)) - .background(Color(0xFF3A3A4E)) + .background(Color.White.copy(alpha = 0.15f)) .clickable { editingIndex = index editingName = name @@ -3065,24 +3160,62 @@ fun EditRoomsDialog( } } - // 完成按钮 - Box( - modifier = Modifier - .fillMaxWidth() - .height(52.dp) - .clip(RoundedCornerShape(14.dp)) - .background( - Brush.linearGradient(listOf(Color(0xFF00D1FF), Color(0xFFB89CFF))) - ) - .clickable { onDismiss() }, - contentAlignment = Alignment.Center + // 底部按钮区域 + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp) ) { - Text( - text = "完成", - color = Color.White, - fontWeight = FontWeight.Bold, - fontSize = 15.sp - ) + // 添加房间按钮 + Box( + modifier = Modifier + .weight(1f) + .height(52.dp) + .clip(RoundedCornerShape(14.dp)) + .background(Color.White.copy(alpha = 0.15f)) + .clickable { + onAddRoom() + onDismiss() + }, + contentAlignment = Alignment.Center + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "+", + color = Color.White, + fontWeight = FontWeight.Bold, + fontSize = 18.sp + ) + Text( + text = "添加房间", + color = Color.White, + fontWeight = FontWeight.Medium, + fontSize = 15.sp + ) + } + } + + // 完成按钮 + Box( + modifier = Modifier + .weight(1f) + .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/ic_humidity.xml b/app/src/main/res/drawable/ic_humidity.xml new file mode 100644 index 0000000..41d82e9 --- /dev/null +++ b/app/src/main/res/drawable/ic_humidity.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_media.xml b/app/src/main/res/drawable/ic_media.xml index b5f30e0..3ac2d0a 100644 --- a/app/src/main/res/drawable/ic_media.xml +++ b/app/src/main/res/drawable/ic_media.xml @@ -1,4 +1,27 @@ - - - + + + + + + + diff --git a/app/src/main/res/drawable/ic_mode_away.xml b/app/src/main/res/drawable/ic_mode_away.xml index 9061809..79adf2e 100644 --- a/app/src/main/res/drawable/ic_mode_away.xml +++ b/app/src/main/res/drawable/ic_mode_away.xml @@ -1,25 +1,20 @@ - - + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + - - - - - - + android:pathData="M7,10 L7,7 C7,4.24 9.24,2 12,2 C14.76,2 17,4.24 17,7 L17,10"/> + + diff --git a/app/src/main/res/drawable/ic_mode_fun.xml b/app/src/main/res/drawable/ic_mode_fun.xml index 0db5bb5..18bae95 100644 --- a/app/src/main/res/drawable/ic_mode_fun.xml +++ b/app/src/main/res/drawable/ic_mode_fun.xml @@ -1,35 +1,42 @@ - - + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + + + + + + + - - + android:pathData="M16,14 C16,14.55 15.55,15 15,15 C14.45,15 14,14.55 14,14 C14,13.45 14.45,13 15,13 C15.55,13 16,13.45 16,14 Z"/> - - - - - - - - - + android:fillColor="#FFFFFFFF" + android:pathData="M19,14 C19,14.55 18.55,15 18,15 C17.45,15 17,14.55 17,14 C17,13.45 17.45,13 18,13 C18.55,13 19,13.45 19,14 Z"/> diff --git a/app/src/main/res/drawable/ic_mode_home.xml b/app/src/main/res/drawable/ic_mode_home.xml index bd23778..34baedf 100644 --- a/app/src/main/res/drawable/ic_mode_home.xml +++ b/app/src/main/res/drawable/ic_mode_home.xml @@ -1,32 +1,28 @@ - - - - + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> - + android:strokeColor="#FFFFFFFF" + android:strokeWidth="2" + android:strokeLineCap="round" + android:strokeLineJoin="round" + android:fillColor="#00000000" + android:pathData="M3,10 L12,3 L21,10"/> + + - - - - + android:strokeColor="#FFFFFFFF" + android:strokeWidth="2" + android:strokeLineJoin="round" + android:fillColor="#00000000" + android:pathData="M9,21 L9,15 Q9,14 10,14 L14,14 Q15,14 15,15 L15,21"/> diff --git a/app/src/main/res/drawable/ic_weather_cloudy.xml b/app/src/main/res/drawable/ic_weather_cloudy.xml new file mode 100644 index 0000000..8126dbc --- /dev/null +++ b/app/src/main/res/drawable/ic_weather_cloudy.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/ic_weather_default.xml b/app/src/main/res/drawable/ic_weather_default.xml new file mode 100644 index 0000000..95b8fe7 --- /dev/null +++ b/app/src/main/res/drawable/ic_weather_default.xml @@ -0,0 +1,39 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_weather_foggy.xml b/app/src/main/res/drawable/ic_weather_foggy.xml new file mode 100644 index 0000000..6634108 --- /dev/null +++ b/app/src/main/res/drawable/ic_weather_foggy.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_weather_rainy.xml b/app/src/main/res/drawable/ic_weather_rainy.xml new file mode 100644 index 0000000..92e8e6f --- /dev/null +++ b/app/src/main/res/drawable/ic_weather_rainy.xml @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_weather_snowy.xml b/app/src/main/res/drawable/ic_weather_snowy.xml new file mode 100644 index 0000000..7c8ea15 --- /dev/null +++ b/app/src/main/res/drawable/ic_weather_snowy.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_weather_sunny.xml b/app/src/main/res/drawable/ic_weather_sunny.xml new file mode 100644 index 0000000..7c94fa4 --- /dev/null +++ b/app/src/main/res/drawable/ic_weather_sunny.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_wind.xml b/app/src/main/res/drawable/ic_wind.xml new file mode 100644 index 0000000..5a478d4 --- /dev/null +++ b/app/src/main/res/drawable/ic_wind.xml @@ -0,0 +1,28 @@ + + + + + + diff --git a/app/src/main/res/drawable/zenhome_wallpaper_1764420805752.png b/app/src/main/res/drawable/zenhome_wallpaper_1764420805752.png new file mode 100644 index 0000000..401beb3 Binary files /dev/null and b/app/src/main/res/drawable/zenhome_wallpaper_1764420805752.png differ diff --git a/app/src/main/res/drawable/zenhome_wallpaper_1764420980064.png b/app/src/main/res/drawable/zenhome_wallpaper_1764420980064.png new file mode 100644 index 0000000..701fe42 Binary files /dev/null and b/app/src/main/res/drawable/zenhome_wallpaper_1764420980064.png differ diff --git a/zenhome-wallpaper-1764395780035.png b/zenhome-wallpaper-1764395780035.png new file mode 100644 index 0000000..fa6c334 Binary files /dev/null and b/zenhome-wallpaper-1764395780035.png differ