111/app/src/main/java/com/example/smarthome/ui/MainScaffold.kt

1659 lines
64 KiB
Kotlin
Raw Normal View History

package com.example.smarthome.ui
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
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.draw.scale
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import com.example.smarthome.R
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView
import android.view.SoundEffectConstants
import kotlinx.coroutines.launch
import kotlinx.coroutines.delay
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.geometry.Offset
import androidx.compose.foundation.layout.offset
import androidx.compose.ui.draw.rotate
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
2025-11-27 02:56:42 +00:00
import com.example.smarthome.data.WeatherService
import com.example.smarthome.data.WeatherInfo
@Composable
fun MainScaffold(
selectedRoom: Int,
onRoomSelect: (Int) -> Unit,
selectedNavItem: Int = 0,
onNavItemSelect: (Int) -> Unit = {},
rooms: List<String> = listOf("总览", "客厅", "厨房", "卧室", "影音室", "游戏房"),
onAddRoom: (String) -> Unit = {},
onDeleteRoom: (Int) -> Unit = {}
) {
Row(modifier = Modifier.fillMaxSize()) {
SideNavRail(
selectedNavItem = selectedNavItem,
onNavItemSelect = onNavItemSelect
)
// 根据选中的导航项显示不同的内容
when (selectedNavItem) {
0 -> DashboardContent(selectedRoom, onRoomSelect, rooms, onAddRoom, onDeleteRoom)
2025-11-27 02:56:42 +00:00
1 -> SceneScreen()
2 -> AutomationScreen()
3 -> StatisticsScreen()
4 -> SecurityScreen()
5 -> SettingsContent()
else -> DashboardContent(selectedRoom, onRoomSelect, rooms, onAddRoom, onDeleteRoom)
}
}
}
@Composable
fun DashboardContent(
selectedRoom: Int,
onRoomSelect: (Int) -> Unit,
rooms: List<String> = listOf("总览", "客厅", "厨房", "卧室", "影音室", "游戏房"),
onAddRoom: (String) -> Unit = {},
onDeleteRoom: (Int) -> Unit = {}
) {
var showAddRoomDialog by remember { mutableStateOf(false) }
var editMode by remember { mutableStateOf(false) }
Box(
modifier = Modifier
.fillMaxSize()
.pointerInput(editMode) {
if (editMode) {
detectTapGestures(
onTap = { editMode = false }
)
}
}
) {
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
TopBar()
RoomTabs(
selectedRoom = selectedRoom,
onRoomSelect = onRoomSelect,
rooms = rooms,
onAddRoomClick = { showAddRoomDialog = true },
onDeleteRoom = onDeleteRoom,
editMode = editMode,
onEditModeChange = { editMode = it }
)
// 根据选中的房间显示不同的内容
RoomContent(selectedRoom = selectedRoom, roomName = rooms.getOrNull(selectedRoom) ?: "总览")
}
if (showAddRoomDialog) {
AddRoomDialog(
onDismiss = { showAddRoomDialog = false },
onConfirm = { roomName ->
onAddRoom(roomName)
showAddRoomDialog = false
}
)
}
}
}
@Composable
fun RoomContent(selectedRoom: Int, roomName: String) {
// 根据房间索引显示不同的内容
when (selectedRoom) {
0 -> OverviewRoomContent() // 总览
else -> SpecificRoomContent(selectedRoom, roomName) // 具体房间
}
}
@Composable
fun OverviewRoomContent() {
Column(modifier = Modifier.fillMaxSize()) {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(16.dp)) {
AirConditionerCard(modifier = Modifier.weight(1f), roomName = "全屋")
UsageStatusChart(modifier = Modifier.weight(1f), roomName = "全屋")
}
2025-11-26 03:43:39 +00:00
LightRow(modifier = Modifier.fillMaxWidth().padding(top = 16.dp, bottom = 16.dp), roomName = "全屋")
ModeButtonsRow(onModeSelected = { })
2025-11-26 03:43:39 +00:00
Spacer(modifier = Modifier.height(24.dp))
// 显示所有房间的设备摘要
Text(
text = "所有设备",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color.White,
2025-11-26 03:43:39 +00:00
modifier = Modifier.padding(bottom = 12.dp)
)
AllDevicesOverview(modifier = Modifier.fillMaxSize())
}
}
@Composable
fun SpecificRoomContent(selectedRoom: Int, roomName: String) {
Column(modifier = Modifier.fillMaxSize()) {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(16.dp)) {
AirConditionerCard(modifier = Modifier.weight(1f), roomName = roomName)
UsageStatusChart(modifier = Modifier.weight(1f), roomName = roomName)
}
2025-11-26 03:43:39 +00:00
LightRow(modifier = Modifier.fillMaxWidth().padding(top = 16.dp, bottom = 16.dp), roomName = roomName)
Spacer(modifier = Modifier.height(24.dp))
Text(
text = "$roomName 设备",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color.White,
2025-11-26 03:43:39 +00:00
modifier = Modifier.padding(bottom = 12.dp)
)
MyDevicesGrid(selectedRoom = selectedRoom, modifier = Modifier.fillMaxSize())
}
}
@Composable
fun AllDevicesOverview(modifier: Modifier = Modifier) {
val allDevices = listOf(
Device("客厅空调", "24°C", R.drawable.ic_ac, true),
Device("客厅灯光", "80%", R.drawable.ic_light, true),
Device("厨房灯光", "60%", R.drawable.ic_light, true),
Device("卧室空调", "22°C", R.drawable.ic_ac, false),
Device("影音室电视", "运行中", R.drawable.ic_media, true),
Device("游戏房主机", "待机中", R.drawable.ic_media, false)
)
LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 140.dp),
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
items(allDevices) { d -> DeviceCard(d) }
}
}
@Composable
fun HeaderHero() {
TopBar()
}
@Composable
fun StatusChips() {
SideNavRail()
}
@Composable
fun Chip(text: String) {
Box(modifier = Modifier.clip(RoundedCornerShape(20.dp)).background(Color(0x331A1A1A)).padding(horizontal = 12.dp, vertical = 8.dp)) {
Text(text = text, color = Color.White)
}
}
@Composable
fun QuickActions() {
Row(modifier = Modifier.fillMaxWidth().padding(24.dp), horizontalArrangement = Arrangement.spacedBy(12.dp)) {
QuickActionCard(
title = "回家模式",
colors = Brush.linearGradient(listOf(Color(0xFFFFD54F), Color(0xFFFF8F00))),
modifier = Modifier.weight(1f),
onClick = { /* TODO: 触发回家场景 */ }
)
QuickActionCard(
title = "出门模式",
colors = Brush.linearGradient(listOf(Color(0xFFFF8A65), Color(0xFFFF7043))),
modifier = Modifier.weight(1f),
onClick = { /* TODO: 触发出门场景 */ }
)
QuickActionCard(
title = "玩乐模式",
colors = Brush.linearGradient(listOf(Color(0xFF6A3DFF), Color(0xFF3A0CA3))),
modifier = Modifier.weight(1f),
onClick = { /* TODO: 触发玩乐场景 */ }
)
}
}
@Composable
fun QuickActionCard(title: String, colors: Brush, modifier: Modifier = Modifier, onClick: () -> Unit) {
val shape = RoundedCornerShape(24.dp)
var pressed by remember { mutableStateOf(false) }
val scale by androidx.compose.animation.core.animateFloatAsState(
targetValue = if (pressed) 0.97f else 1f,
animationSpec = androidx.compose.animation.core.spring(),
label = "quick_action_scale"
)
Box(
modifier = modifier
.height(88.dp)
.shadow(12.dp, shape, false)
.clip(shape)
.background(colors)
.scale(scale)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
pressed = !pressed
onClick()
}
.padding(16.dp),
contentAlignment = Alignment.BottomStart
) {
Text(text = title, fontWeight = FontWeight.Medium, color = Color.White)
}
}
@Composable
fun RoomTabs(
selectedRoom: Int,
onRoomSelect: (Int) -> Unit,
rooms: List<String> = listOf("总览", "客厅", "厨房", "卧室", "影音室", "游戏房"),
onAddRoomClick: () -> Unit = {},
onDeleteRoom: (Int) -> Unit = {},
editMode: Boolean = false,
onEditModeChange: (Boolean) -> Unit = {}
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
// 可滚动的房间标签区域
Row(
modifier = Modifier
.weight(1f)
.horizontalScroll(rememberScrollState()),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
rooms.forEachIndexed { index, name ->
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)
}
)
}
}
// 固定的添加按钮
GradientButton("+ 添加", onClick = onAddRoomClick)
}
}
@Composable
fun RoomTab(
name: String,
selected: Boolean,
editMode: Boolean,
canDelete: Boolean,
onClick: () -> Unit,
onLongClick: () -> Unit,
onDelete: () -> 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"
)
Box(
modifier = Modifier
.scale(scale)
.then(
if (editMode && canDelete) {
Modifier.rotate(rotation)
} else {
Modifier
}
)
.pointerInput(Unit) {
detectTapGestures(
onTap = { onClick() },
onLongPress = {
if (canDelete) {
onLongClick()
}
}
)
}
) {
val base = Modifier.clip(RoundedCornerShape(20.dp))
val mod = if (selected) {
base.background(brush = Brush.linearGradient(listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF))))
} else {
2025-11-27 02:56:42 +00:00
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
)
}
}
}
}
@Composable
fun DeviceGrid() {
val devices = listOf(
Device("空调", "室内 25℃", R.drawable.ic_ac, true),
Device("智能开关", "待机中", R.drawable.ic_light, false),
Device("吊灯", "6500K", R.drawable.ic_light, true),
Device("扫地机", "清扫中", R.drawable.ic_media, true),
Device("空气净化", "运行中", R.drawable.ic_security, true),
Device("饮水机", "待机中", R.drawable.ic_media, false)
)
LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 140.dp), modifier = Modifier.fillMaxSize().padding(24.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
items(devices) { d -> DeviceCard(d) }
}
}
data class Device(val name: String, val sub: String, val icon: Int, val on: Boolean)
@Composable
fun DeviceCard(d: Device) {
var active by remember { mutableStateOf(d.on) }
2025-11-27 02:56:42 +00:00
Box(
modifier = Modifier
.height(120.dp)
.clip(RoundedCornerShape(24.dp))
.background(Color(0x50121212))
.padding(16.dp)
) {
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp)) {
Image(painter = painterResource(id = d.icon), contentDescription = null, modifier = Modifier.size(40.dp))
Column {
Text(text = d.name, fontWeight = FontWeight.SemiBold)
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp)) {
Box(modifier = Modifier.size(8.dp).clip(RoundedCornerShape(4.dp)).background(if (active) Color(0xFF00E676) else Color(0xFF9AA0A6)))
Text(text = d.sub, color = Color(0xFF9AA0A6))
}
}
}
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Text(text = if (active) "运行中" else "待机中", color = if (active) Color(0xFF00E676) else Color(0xFF9AA0A6))
Toggle(active) { active = it }
}
}
}
}
@Composable
fun Toggle(on: Boolean, change: (Boolean) -> Unit) {
Box(modifier = Modifier.size(48.dp, 28.dp).clip(RoundedCornerShape(18.dp)).background(if (on) Color(0xFF3A0CA3) else Color(0x33222222)).clickable { change(!on) }.padding(4.dp)) {
Box(modifier = Modifier.size(20.dp).clip(RoundedCornerShape(10.dp)).background(Color.White).align(if (on) Alignment.CenterEnd else Alignment.CenterStart))
}
}
@Composable
fun BottomNav() {
2025-11-27 02:56:42 +00:00
Row(modifier = Modifier.fillMaxWidth().padding(16.dp).clip(RoundedCornerShape(28.dp)).background(Color(0x50121212)).padding(8.dp), horizontalArrangement = Arrangement.SpaceEvenly) {
NavItem("首页", true)
NavItem("设备", false)
NavItem("我的", false)
}
}
@Composable
fun NavItem(title: String, selected: Boolean) {
Box(modifier = Modifier.clip(RoundedCornerShape(20.dp)).background(if (selected) Color(0x55121212) else Color.Transparent).padding(horizontal = 18.dp, vertical = 10.dp)) {
Text(text = title, color = if (selected) Color.White else Color(0xFFB0B0B0))
}
}
@Composable
fun SideNavRail(selectedNavItem: Int = 0, onNavItemSelect: (Int) -> Unit = {}) {
Column(modifier = Modifier.width(120.dp).fillMaxHeight().padding(12.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
Spacer(modifier = Modifier.height(48.dp))
NavRailItem("控制台", R.drawable.ic_dashboard, selectedNavItem == 0, onClick = { onNavItemSelect(0) })
2025-11-27 02:56:42 +00:00
NavRailItem("场景", R.drawable.ic_scene, selectedNavItem == 1, onClick = { onNavItemSelect(1) })
NavRailItem("自动化", R.drawable.ic_automation, selectedNavItem == 2, onClick = { onNavItemSelect(2) })
NavRailItem("统计", R.drawable.ic_statistics, selectedNavItem == 3, onClick = { onNavItemSelect(3) })
NavRailItem("安全", R.drawable.ic_security, selectedNavItem == 4, onClick = { onNavItemSelect(4) })
NavRailItem("设置", R.drawable.ic_settings, selectedNavItem == 5, onClick = { onNavItemSelect(5) })
}
}
@Composable
fun NavRailItem(text: String, iconRes: Int, selected: Boolean, onClick: () -> Unit = {}) {
val scale by androidx.compose.animation.core.animateFloatAsState(
targetValue = if (selected) 1.05f else 1f,
animationSpec = androidx.compose.animation.core.spring(),
label = "nav_scale"
)
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(6.dp),
modifier = Modifier
.scale(scale)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) { onClick() }
.padding(vertical = 4.dp)
) {
Box(
modifier = Modifier
.size(48.dp)
.shadow(if (selected) 8.dp else 0.dp, RoundedCornerShape(14.dp))
.clip(RoundedCornerShape(14.dp))
.background(
if (selected) {
Brush.linearGradient(
listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF))
)
} else {
Brush.linearGradient(
listOf(Color(0x22FFFFFF), Color(0x22FFFFFF))
)
}
),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(id = iconRes),
contentDescription = text,
modifier = Modifier.size(24.dp),
colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(
if (selected) Color.Black else Color(0xFFB0B0B0)
)
)
}
Text(
text = text,
color = if (selected) Color.White else Color(0xFFB0B0B0),
fontSize = 13.sp,
fontWeight = if (selected) FontWeight.SemiBold else FontWeight.Normal,
maxLines = 1
)
}
}
@Composable
fun TopBar() {
2025-11-27 02:56:42 +00:00
var weatherInfo by remember { mutableStateOf(WeatherService.getSimulatedWeather()) }
var isLoading by remember { mutableStateOf(true) }
LaunchedEffect(Unit) {
try {
weatherInfo = WeatherService.getWeather()
} catch (e: Exception) {
// 使用模拟数据
}
isLoading = false
}
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) {
Text(text = "控制台", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = Color.White)
Row(horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically) {
2025-11-27 02:56:42 +00:00
// 天气信息卡片
WeatherCard(weatherInfo = weatherInfo, isLoading = isLoading)
GradientButton("+ 添加设备")
}
}
}
2025-11-27 02:56:42 +00:00
@Composable
fun WeatherCard(weatherInfo: WeatherInfo, isLoading: Boolean) {
Box(
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.background(Color(0x50121212))
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
// 温度和天气
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = if (isLoading) "--" else "${weatherInfo.temperature}°C",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
Text(
text = if (isLoading) "加载中" else weatherInfo.weather,
fontSize = 12.sp,
color = Color(0xFF9AA0A6)
)
}
// 分隔线
Box(
modifier = Modifier
.width(1.dp)
.height(30.dp)
.background(Color(0x33FFFFFF))
)
// 湿度
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = if (isLoading) "--" else "${weatherInfo.humidity}%",
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
color = Color.White
)
Text(
text = "湿度",
fontSize = 11.sp,
color = Color(0xFF9AA0A6)
)
}
// 分隔线
Box(
modifier = Modifier
.width(1.dp)
.height(30.dp)
.background(Color(0x33FFFFFF))
)
// 空气质量
Column(horizontalAlignment = Alignment.CenterHorizontally) {
val aqiColor = when {
weatherInfo.aqi <= 50 -> Color(0xFF00E676)
weatherInfo.aqi <= 100 -> Color(0xFFFFEB3B)
weatherInfo.aqi <= 150 -> Color(0xFFFF9800)
else -> Color(0xFFFF5252)
}
Text(
text = if (isLoading) "--" else weatherInfo.airQuality,
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
color = aqiColor
)
Text(
text = if (isLoading) "AQI" else "AQI ${weatherInfo.aqi}",
fontSize = 11.sp,
color = Color(0xFF9AA0A6)
)
}
// 城市
Text(
text = weatherInfo.city,
fontSize = 12.sp,
color = Color(0xFFB0B0B0)
)
}
}
}
@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)
}
}
@Composable
fun MyDevicesGrid(selectedRoom: Int, modifier: Modifier = Modifier) {
val devices = when (selectedRoom) {
1 -> listOf( // 客厅
Device("智能电视", "运行中", R.drawable.ic_media, true),
Device("音响", "待机中", R.drawable.ic_media, false),
Device("空调", "24°C", R.drawable.ic_ac, true),
Device("主灯", "80%", R.drawable.ic_light, true),
Device("窗帘", "已打开", R.drawable.ic_curtain, true),
Device("插座", "运行中", R.drawable.ic_light, true)
)
2 -> listOf( // 厨房
Device("冰箱", "运行中", R.drawable.ic_ac, true),
Device("微波炉", "待机中", R.drawable.ic_media, false),
Device("油烟机", "待机中", R.drawable.ic_media, false),
Device("灯光", "60%", R.drawable.ic_light, true)
)
3 -> listOf( // 卧室
Device("空调", "22°C", R.drawable.ic_ac, true),
Device("床头灯", "40%", R.drawable.ic_light, true),
Device("窗帘", "已关闭", R.drawable.ic_curtain, false),
Device("加湿器", "运行中", R.drawable.ic_media, true),
Device("空气净化器", "运行中", R.drawable.ic_security, true)
)
4 -> listOf( // 影音室
Device("投影仪", "运行中", R.drawable.ic_media, true),
Device("功放", "运行中", R.drawable.ic_media, true),
Device("音响系统", "运行中", R.drawable.ic_media, true),
Device("灯光", "20%", R.drawable.ic_light, true),
Device("窗帘", "已关闭", R.drawable.ic_curtain, false),
Device("空调", "23°C", R.drawable.ic_ac, true)
)
5 -> listOf( // 游戏房
Device("游戏主机", "运行中", R.drawable.ic_media, true),
Device("显示器", "运行中", R.drawable.ic_media, true),
Device("RGB灯带", "运行中", R.drawable.ic_light, true),
Device("空调", "20°C", R.drawable.ic_ac, true),
Device("路由器", "运行中", R.drawable.ic_security, true)
)
else -> emptyList()
}
LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 140.dp),
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
items(devices) { d -> DeviceCard(d) }
}
}
@Composable
fun UsageStatusChart(modifier: Modifier = Modifier, roomName: String = "房间") {
// 根据房间生成不同的数据
val values = remember(roomName) {
2025-11-27 02:56:42 +00:00
List(12) { (15..45).random().toFloat() }
}
2025-11-27 02:56:42 +00:00
val maxValue = remember(values) { values.maxOrNull() ?: 45f }
val totalPower = remember(roomName) { (15.0 + Math.random() * 30).toString().take(4) }
val totalHours = remember(roomName) { (10 + (Math.random() * 40).toInt()) }
2025-11-27 02:56:42 +00:00
val activeDevices = remember(roomName) { (3 + (Math.random() * 8).toInt()) }
val trend = remember(roomName) { if (Math.random() > 0.5) "" else "" }
val trendPercent = remember(roomName) { (5 + (Math.random() * 15).toInt()) }
2025-11-27 02:56:42 +00:00
Box(
modifier = modifier
.height(220.dp)
.clip(RoundedCornerShape(24.dp))
.background(
Brush.linearGradient(
colors = listOf(
Color(0x60121212),
Color(0x50121212)
)
)
)
.border(
width = 1.dp,
brush = Brush.linearGradient(
colors = listOf(
Color(0x22FFFFFF),
Color(0x0AFFFFFF)
)
),
shape = RoundedCornerShape(24.dp)
)
.padding(16.dp)
) {
Column(
verticalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier.fillMaxSize()
) {
// 标题区域
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column {
Text(
text = "能耗统计",
fontWeight = FontWeight.Bold,
color = Color.White,
fontSize = 16.sp
)
Text(
text = roomName,
fontSize = 12.sp,
color = Color(0xFF9AA0A6)
)
}
// 趋势指示器
Box(
modifier = Modifier
.clip(RoundedCornerShape(12.dp))
.background(
if (trend == "") Color(0x33FF5252) else Color(0x3300E676)
)
.padding(horizontal = 10.dp, vertical = 6.dp)
) {
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = trend,
color = if (trend == "") Color(0xFFFF5252) else Color(0xFF00E676),
fontSize = 14.sp,
fontWeight = FontWeight.Bold
)
Text(
text = "${trendPercent}%",
color = if (trend == "") Color(0xFFFF5252) else Color(0xFF00E676),
fontSize = 12.sp,
fontWeight = FontWeight.Medium
)
}
}
}
2025-11-27 02:56:42 +00:00
// 数据指标区域
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
// 总消耗
Column(
modifier = Modifier.weight(1f)
) {
Row(
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = totalPower,
color = Color(0xFFA9F0FF),
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
Text(
text = "kWh",
color = Color(0xFF9AA0A6),
fontSize = 12.sp,
modifier = Modifier.padding(bottom = 2.dp)
)
}
Text(
text = "总消耗",
color = Color(0xFF9AA0A6),
fontSize = 11.sp
)
}
2025-11-27 02:56:42 +00:00
// 运行时长
Column(
modifier = Modifier.weight(1f)
) {
Row(
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = "$totalHours",
color = Color(0xFFB89CFF),
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
Text(
text = "小时",
color = Color(0xFF9AA0A6),
fontSize = 12.sp,
modifier = Modifier.padding(bottom = 2.dp)
)
}
Text(
text = "运行时长",
color = Color(0xFF9AA0A6),
fontSize = 11.sp
)
}
// 活跃设备
Column(
modifier = Modifier.weight(1f)
) {
Row(
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = "$activeDevices",
color = Color(0xFF00E676),
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
Text(
text = "",
color = Color(0xFF9AA0A6),
fontSize = 12.sp,
modifier = Modifier.padding(bottom = 2.dp)
)
}
Text(
text = "活跃设备",
color = Color(0xFF9AA0A6),
fontSize = 11.sp
)
}
}
2025-11-27 02:56:42 +00:00
Spacer(modifier = Modifier.height(4.dp))
// 图表区域
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomCenter),
horizontalArrangement = Arrangement.spacedBy(6.dp),
verticalAlignment = Alignment.Bottom
) {
values.forEachIndexed { index, v ->
val normalizedHeight = (v / maxValue) * 60
val isHighlight = index == values.size - 1
Box(
modifier = Modifier
.weight(1f)
.height(normalizedHeight.dp)
.clip(RoundedCornerShape(topStart = 4.dp, topEnd = 4.dp))
.background(
if (isHighlight) {
Brush.verticalGradient(
colors = listOf(
Color(0xFFA9F0FF),
Color(0xFF00D1FF)
)
)
} else {
Brush.verticalGradient(
colors = listOf(
Color(0xFF3A3A3A),
Color(0xFF2A2A2A)
)
)
}
)
)
}
}
// 时间标签
Row(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomCenter)
.offset(y = 16.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = "12h前",
fontSize = 10.sp,
color = Color(0xFF666666)
)
Text(
text = "现在",
fontSize = 10.sp,
color = Color(0xFFA9F0FF),
fontWeight = FontWeight.Medium
)
}
}
}
}
}
@Composable
fun AirConditionerCard(modifier: Modifier = Modifier, roomName: String = "房间") {
var temp by remember { mutableStateOf(24f) }
var isOn by remember { mutableStateOf(true) }
2025-11-27 02:56:42 +00:00
Box(modifier = modifier.height(220.dp).clip(RoundedCornerShape(24.dp)).background(Color(0x50121212)).padding(16.dp)) {
2025-11-26 03:43:39 +00:00
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
Row(horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
Column {
2025-11-26 03:43:39 +00:00
Text(text = "空调", fontWeight = FontWeight.SemiBold, color = Color.White, fontSize = 18.sp)
Text(text = roomName, fontSize = 12.sp, color = Color(0xFF9AA0A6))
}
Switch(checked = isOn, onCheckedChange = { isOn = it })
}
2025-11-26 03:43:39 +00:00
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "${temp.toInt()}°C",
style = MaterialTheme.typography.headlineLarge,
color = Color.White,
fontWeight = FontWeight.Bold
)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
// 降温按钮
Box(
modifier = Modifier
.size(40.dp)
.clip(RoundedCornerShape(12.dp))
2025-11-27 02:56:42 +00:00
.background(Color(0x55FFFFFF))
2025-11-26 03:43:39 +00:00
.clickable(enabled = isOn && temp > 16f) {
if (temp > 16f) temp -= 1f
},
contentAlignment = Alignment.Center
) {
Text(text = "", color = Color.White, fontSize = 24.sp, fontWeight = FontWeight.Bold)
}
// 升温按钮
Box(
modifier = Modifier
.size(40.dp)
.clip(RoundedCornerShape(12.dp))
2025-11-27 02:56:42 +00:00
.background(Color(0x55FFFFFF))
2025-11-26 03:43:39 +00:00
.clickable(enabled = isOn && temp < 32f) {
if (temp < 32f) temp += 1f
},
contentAlignment = Alignment.Center
) {
Text(text = "+", color = Color.White, fontSize = 24.sp, fontWeight = FontWeight.Bold)
}
}
}
// 自定义滑动条
CustomSlider(
value = temp,
onValueChange = { temp = it },
valueRange = 16f..32f,
enabled = isOn
)
2025-11-26 03:43:39 +00:00
// 温度标记
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(text = "16°C", fontSize = 12.sp, color = Color(0xFF9AA0A6))
Text(text = "24°C", fontSize = 12.sp, color = Color(0xFF9AA0A6))
Text(text = "32°C", fontSize = 12.sp, color = Color(0xFF9AA0A6))
}
}
}
}
2025-11-26 03:43:39 +00:00
@OptIn(androidx.compose.material3.ExperimentalMaterial3Api::class)
@Composable
fun CustomSlider(
value: Float,
onValueChange: (Float) -> Unit,
valueRange: ClosedFloatingPointRange<Float>,
enabled: Boolean = true
) {
val interactionSource = remember { MutableInteractionSource() }
Box(
modifier = Modifier
.fillMaxWidth()
.height(32.dp),
contentAlignment = Alignment.Center
) {
Slider(
value = value,
onValueChange = onValueChange,
valueRange = valueRange,
enabled = enabled,
interactionSource = interactionSource,
colors = androidx.compose.material3.SliderDefaults.colors(
thumbColor = Color.White,
activeTrackColor = Brush.linearGradient(
listOf(Color(0xFF00D1FF), Color(0xFFA9F0FF))
).let { Color(0xFF00D1FF) },
2025-11-27 02:56:42 +00:00
inactiveTrackColor = Color(0x55FFFFFF),
2025-11-26 03:43:39 +00:00
disabledThumbColor = Color(0x66FFFFFF),
2025-11-27 02:56:42 +00:00
disabledActiveTrackColor = Color(0x55FFFFFF),
disabledInactiveTrackColor = Color(0x55FFFFFF)
2025-11-26 03:43:39 +00:00
),
modifier = Modifier
.fillMaxWidth()
.height(24.dp),
thumb = {
Box(
modifier = Modifier
.size(20.dp)
.shadow(4.dp, RoundedCornerShape(10.dp))
.clip(RoundedCornerShape(10.dp))
.background(Color.White)
)
},
track = { sliderState ->
val fraction = (sliderState.value - valueRange.start) / (valueRange.endInclusive - valueRange.start)
Box(
modifier = Modifier
.fillMaxWidth()
.height(6.dp)
.clip(RoundedCornerShape(3.dp))
) {
// 未激活轨道
Box(
modifier = Modifier
.fillMaxSize()
2025-11-27 02:56:42 +00:00
.background(if (enabled) Color(0x55FFFFFF) else Color(0x55FFFFFF))
2025-11-26 03:43:39 +00:00
)
// 激活轨道
Box(
modifier = Modifier
.fillMaxWidth(fraction)
.fillMaxHeight()
.background(
if (enabled) {
Brush.linearGradient(
listOf(Color(0xFF00D1FF), Color(0xFFA9F0FF))
)
} else {
Brush.linearGradient(
2025-11-27 02:56:42 +00:00
listOf(Color(0x55FFFFFF), Color(0x55FFFFFF))
2025-11-26 03:43:39 +00:00
)
}
)
)
}
}
)
}
}
@Composable
fun LightList(modifier: Modifier = Modifier) {
val lights = listOf(
"灯光 1" to 0.6f,
"灯光 2" to 0.8f,
"灯光 3" to 0.45f,
"灯光 4" to 0.6f,
"灯光 5" to 0.6f
)
Column(modifier = modifier.fillMaxHeight(), verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text(text = "灯光", fontWeight = FontWeight.SemiBold)
lights.forEach { (name, percent) ->
2025-11-27 02:56:42 +00:00
Row(horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clip(RoundedCornerShape(16.dp)).background(Color(0x50121212)).padding(12.dp)) {
Row(horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically) {
2025-11-27 02:56:42 +00:00
Box(modifier = Modifier.size(36.dp).clip(RoundedCornerShape(10.dp)).background(Color(0x50121212)))
Column { Text(text = name); Text(text = "${(percent * 100).toInt()}%", color = Color(0xFF9AA0A6)) }
}
DotsProgress(percent)
}
}
}
}
@Composable
2025-11-26 03:43:39 +00:00
fun DotsProgress(percent: Float, onPercentChange: (Float) -> Unit = {}, enabled: Boolean = true) {
val total = 12
2025-11-26 03:43:39 +00:00
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.pointerInput(enabled) {
if (enabled) {
detectTapGestures { offset ->
// 计算点击的是第几个点
val dotWidth = 6.dp.toPx()
val spacing = 4.dp.toPx()
val totalWidth = (dotWidth + spacing) * total - spacing
val clickedIndex = ((offset.x / totalWidth) * total).toInt().coerceIn(0, total - 1)
// 更新百分比
val newPercent = (clickedIndex + 1).toFloat() / total
onPercentChange(newPercent)
}
}
}
) {
repeat(total) { i ->
val filled = i < (percent * total).toInt()
2025-11-26 03:43:39 +00:00
Box(
modifier = Modifier
.size(8.dp)
.clip(RoundedCornerShape(4.dp))
.background(if (filled) Color(0xFFA9F0FF) else Color(0xFF3A3A3A))
.clickable(enabled = enabled) {
// 点击某个点直接设置到该亮度
val newPercent = (i + 1).toFloat() / total
onPercentChange(newPercent)
}
)
}
}
}
enum class Mode { HOME, AWAY, FUN }
@Composable
fun ModeButtonsRow(onModeSelected: (Mode) -> Unit) {
BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
val density = LocalDensity.current
val buttonHeight = when {
maxWidth >= 1024.dp -> with(density) { 500f.toDp() }
maxWidth >= 600.dp -> with(density) { 460f.toDp() }
else -> with(density) { 160f.toDp() }
}
var selected by remember { mutableStateOf<Mode?>(null) }
val isVertical = maxWidth < 480.dp
val containerModifier = Modifier.fillMaxWidth()
val arrangement = Arrangement.spacedBy(16.dp)
val items = listOf(
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))), "玩乐"),
)
if (isVertical) {
Column(modifier = containerModifier, verticalArrangement = arrangement) {
items.forEach { (mode, brush, label) ->
ModeButton(
text = label,
modifier = Modifier.fillMaxWidth(),
height = buttonHeight,
brush = brush,
selected = selected == mode,
onClick = {
selected = mode
onModeSelected(mode)
}
)
}
}
} else {
Row(modifier = containerModifier, horizontalArrangement = arrangement) {
items.forEach { (mode, brush, label) ->
ModeButton(
text = label,
modifier = Modifier.weight(1f),
height = buttonHeight,
brush = brush,
selected = selected == mode,
onClick = {
selected = mode
onModeSelected(mode)
}
)
}
}
}
}
}
@Composable
fun ModeButton(
text: String,
modifier: Modifier,
height: androidx.compose.ui.unit.Dp,
brush: Brush,
selected: Boolean,
onClick: () -> Unit
) {
val shape = RoundedCornerShape(24.dp)
val interaction = remember { MutableInteractionSource() }
var pressed by remember { mutableStateOf(false) }
2025-11-27 02:56:42 +00:00
// 根据文本确定图标
val iconRes = when (text) {
"回家" -> R.drawable.ic_mode_home
"出门" -> R.drawable.ic_mode_away
"玩乐" -> R.drawable.ic_mode_fun
else -> R.drawable.ic_dashboard
}
val scale by androidx.compose.animation.core.animateFloatAsState(
targetValue = if (pressed) 0.96f 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 elevation by androidx.compose.animation.core.animateDpAsState(
targetValue = if (selected) 16.dp else if (pressed) 4.dp else 8.dp,
animationSpec = androidx.compose.animation.core.tween(300),
label = "elevation"
)
2025-11-27 02:56:42 +00:00
val iconScale by androidx.compose.animation.core.animateFloatAsState(
targetValue = if (selected) 1.1f else 1f,
animationSpec = androidx.compose.animation.core.spring(
dampingRatio = androidx.compose.animation.core.Spring.DampingRatioMediumBouncy
),
label = "icon_scale"
)
val view = LocalView.current
LaunchedEffect(interaction) {
interaction.interactions.collect { i ->
when (i) {
is PressInteraction.Press -> pressed = true
is PressInteraction.Release, is PressInteraction.Cancel -> pressed = false
}
}
}
Box(
modifier = modifier
.height(height)
.scale(scale)
2025-11-27 02:56:42 +00:00
.clip(shape)
.then(
if (selected) {
2025-11-27 02:56:42 +00:00
Modifier
.shadow(elevation, shape, clip = false)
.background(brush, shape)
.border(
width = 2.dp,
brush = Brush.linearGradient(
listOf(
Color(0x99FFFFFF),
Color(0x66FFFFFF)
)
),
shape = shape
)
} else {
2025-11-27 02:56:42 +00:00
Modifier.background(Color(0x50121212))
}
)
.clickable(indication = null, interactionSource = interaction) {
view.playSoundEffect(SoundEffectConstants.CLICK)
onClick()
}
.padding(24.dp),
contentAlignment = Alignment.Center
) {
2025-11-27 02:56:42 +00:00
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
// 图标容器
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
)
}
}
}
@Composable
fun LightRow(modifier: Modifier = Modifier, roomName: String = "房间") {
// 根据房间生成不同的灯光数据
val lights = remember(roomName) {
listOf(
"$roomName 主灯" to (0.5f + Math.random().toFloat() * 0.4f),
"$roomName 辅灯" to (0.5f + Math.random().toFloat() * 0.4f)
)
}
Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(16.dp)) {
lights.forEach { (name, percent) ->
LightCard(name = name, percent = percent, modifier = Modifier.weight(1f))
}
}
}
@Composable
fun LightCard(name: String, percent: Float, modifier: Modifier = Modifier) {
2025-11-26 03:43:39 +00:00
var brightness by remember { mutableStateOf(percent) }
2025-11-27 02:56:42 +00:00
Box(modifier = modifier.height(100.dp).clip(RoundedCornerShape(16.dp)).background(Color(0x50121212)).padding(12.dp)) {
Row(horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
Row(horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically) {
2025-11-26 03:43:39 +00:00
// 灯光图标 - 带发光效果
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
)
} else {
Brush.linearGradient(
listOf(
Color(0xFF3A3A3A),
Color(0xFF2A2A2A)
)
)
}
),
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)
}
)
)
}
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
)
}
}
2025-11-26 03:43:39 +00:00
DotsProgress(
percent = brightness,
onPercentChange = { brightness = it },
enabled = true
)
}
}
}
@Composable
fun AddRoomDialog(
onDismiss: () -> Unit,
onConfirm: (String) -> Unit
) {
var roomName by remember { mutableStateOf("") }
Box(
modifier = Modifier
.fillMaxSize()
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) { onDismiss() },
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.width(400.dp)
.clip(RoundedCornerShape(24.dp))
.background(Color(0xFF1E1E1E))
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) { /* 阻止点击穿透 */ }
.padding(24.dp)
) {
Column(
verticalArrangement = Arrangement.spacedBy(20.dp)
) {
Text(
text = "添加房间",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
androidx.compose.material3.TextField(
value = roomName,
onValueChange = { roomName = it },
placeholder = { Text("请输入房间名称", color = Color(0xFF9AA0A6)) },
modifier = Modifier.fillMaxWidth(),
colors = androidx.compose.material3.TextFieldDefaults.colors(
2025-11-27 02:56:42 +00:00
focusedContainerColor = Color(0x55FFFFFF),
unfocusedContainerColor = Color(0x55FFFFFF),
focusedTextColor = Color.White,
unfocusedTextColor = Color.White,
cursorColor = Color(0xFFA9F0FF),
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(12.dp),
singleLine = true
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
Box(
modifier = Modifier
.weight(1f)
.height(48.dp)
.clip(RoundedCornerShape(12.dp))
2025-11-27 02:56:42 +00:00
.background(Color(0x55FFFFFF))
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) { onDismiss() },
contentAlignment = Alignment.Center
) {
Text(
text = "取消",
color = Color(0xFFB0B0B0),
fontWeight = FontWeight.Medium
)
}
Box(
modifier = Modifier
.weight(1f)
.height(48.dp)
.clip(RoundedCornerShape(12.dp))
.background(
Brush.linearGradient(
listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF))
)
)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() },
enabled = roomName.isNotBlank()
) {
if (roomName.isNotBlank()) {
onConfirm(roomName.trim())
}
}
.alpha(if (roomName.isNotBlank()) 1f else 0.5f),
contentAlignment = Alignment.Center
) {
Text(
text = "确定",
color = Color.Black,
fontWeight = FontWeight.Bold
)
}
}
}
}
}
}