增加天气 ip 查询

This commit is contained in:
zzh 2025-11-27 10:56:42 +08:00
parent 6393df34ea
commit 0b4fbe3be2
7 changed files with 573 additions and 96 deletions

View File

@ -68,6 +68,9 @@ dependencies {
implementation("androidx.compose.material3:material3-window-size-class:1.3.0")
implementation("com.github.Dimezis:BlurView:version-3.2.0")
implementation("com.google.code.gson:gson:2.10.1")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.google.android.gms:play-services-location:21.0.1")
debugImplementation("com.squareup.leakcanary:leakcanary-android:2.12")

View File

@ -1,8 +1,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:name=".SmartHomeApp"
android:icon="@mipmap/ic_launcher"
android:label="SmartHome"
android:label="TZSmartHome"
android:theme="@style/Theme.SmartHome">
<activity android:name=".MainActivity"
android:exported="true">

View File

@ -66,6 +66,8 @@ import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import com.example.smarthome.data.WeatherService
import com.example.smarthome.data.WeatherInfo
@Composable
fun MainScaffold(
@ -86,6 +88,10 @@ fun MainScaffold(
// 根据选中的导航项显示不同的内容
when (selectedNavItem) {
0 -> DashboardContent(selectedRoom, onRoomSelect, rooms, onAddRoom, onDeleteRoom)
1 -> SceneScreen()
2 -> AutomationScreen()
3 -> StatisticsScreen()
4 -> SecurityScreen()
5 -> SettingsContent()
else -> DashboardContent(selectedRoom, onRoomSelect, rooms, onAddRoom, onDeleteRoom)
}
@ -394,7 +400,7 @@ fun RoomTab(
val mod = if (selected) {
base.background(brush = Brush.linearGradient(listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF))))
} else {
base.background(color = Color(0x22121212))
base.background(color = Color(0x50121212))
}
Box(
@ -454,7 +460,13 @@ data class Device(val name: String, val sub: String, val icon: Int, val on: Bool
@Composable
fun DeviceCard(d: Device) {
var active by remember { mutableStateOf(d.on) }
Box(modifier = Modifier.height(120.dp).clip(RoundedCornerShape(24.dp)).background(Color(0x26121212)).shadow(12.dp, RoundedCornerShape(24.dp), true).padding(16.dp)) {
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))
@ -483,7 +495,7 @@ fun Toggle(on: Boolean, change: (Boolean) -> Unit) {
@Composable
fun BottomNav() {
Row(modifier = Modifier.fillMaxWidth().padding(16.dp).clip(RoundedCornerShape(28.dp)).background(Color(0x33121212)).padding(8.dp), horizontalArrangement = Arrangement.SpaceEvenly) {
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)
@ -502,10 +514,10 @@ 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) })
NavRailItem("最近", R.drawable.ic_recent, selectedNavItem == 1, onClick = { onNavItemSelect(1) })
NavRailItem("收藏", R.drawable.ic_bookmark, selectedNavItem == 2, onClick = { onNavItemSelect(2) })
NavRailItem("下载", R.drawable.ic_download, selectedNavItem == 3, onClick = { onNavItemSelect(3) })
NavRailItem("支持", R.drawable.ic_support, selectedNavItem == 4, onClick = { onNavItemSelect(4) })
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) })
}
}
@ -568,17 +580,117 @@ fun NavRailItem(text: String, iconRes: Int, selected: Boolean, onClick: () -> Un
@Composable
fun TopBar() {
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) {
Box(modifier = Modifier.width(280.dp).height(40.dp).clip(RoundedCornerShape(20.dp)).background(Color(0x22121212)).padding(horizontal = 16.dp), contentAlignment = Alignment.CenterStart) {
Text(text = "搜索关键词", color = Color(0xFF9AA0A6))
}
// 天气信息卡片
WeatherCard(weatherInfo = weatherInfo, isLoading = isLoading)
GradientButton("+ 添加设备")
}
}
}
@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) {
@ -641,30 +753,246 @@ fun MyDevicesGrid(selectedRoom: Int, modifier: Modifier = Modifier) {
fun UsageStatusChart(modifier: Modifier = Modifier, roomName: String = "房间") {
// 根据房间生成不同的数据
val values = remember(roomName) {
List(10) { (10..35).random().toFloat() }
List(12) { (15..45).random().toFloat() }
}
val totalPower = remember(roomName) { (15.0 + Math.random() * 30).toString().take(5) }
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()) }
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()) }
Box(modifier = modifier.height(220.dp).clip(RoundedCornerShape(24.dp)).background(Color(0x26121212)).padding(16.dp)) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Column {
Text(text = "使用状态", fontWeight = FontWeight.SemiBold, color = Color.White)
Text(text = roomName, fontSize = 12.sp, color = Color(0xFF9AA0A6))
}
Row(horizontalArrangement = Arrangement.spacedBy(24.dp)) {
Column {
Text(text = "${totalPower}kWh", color = Color.White)
Text(text = "总消耗", color = Color(0xFF9AA0A6), fontSize = 12.sp)
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)
)
}
Column {
Text(text = "${totalHours}h", color = Color.White)
Text(text = "总时长", color = Color(0xFF9AA0A6), fontSize = 12.sp)
// 趋势指示器
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
)
}
}
}
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.Bottom) {
values.forEach { v ->
Box(modifier = Modifier.weight(1f).height(v.dp).clip(RoundedCornerShape(6.dp)).background(Color(0xFF2A2A2A)))
// 数据指标区域
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
)
}
// 运行时长
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
)
}
}
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
)
}
}
}
@ -676,7 +1004,7 @@ fun AirConditionerCard(modifier: Modifier = Modifier, roomName: String = "房间
var temp by remember { mutableStateOf(24f) }
var isOn by remember { mutableStateOf(true) }
Box(modifier = modifier.height(220.dp).clip(RoundedCornerShape(24.dp)).background(Color(0x26121212)).padding(16.dp)) {
Box(modifier = modifier.height(220.dp).clip(RoundedCornerShape(24.dp)).background(Color(0x50121212)).padding(16.dp)) {
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
Row(horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
Column {
@ -704,7 +1032,7 @@ fun AirConditionerCard(modifier: Modifier = Modifier, roomName: String = "房间
modifier = Modifier
.size(40.dp)
.clip(RoundedCornerShape(12.dp))
.background(Color(0x33FFFFFF))
.background(Color(0x55FFFFFF))
.clickable(enabled = isOn && temp > 16f) {
if (temp > 16f) temp -= 1f
},
@ -718,7 +1046,7 @@ fun AirConditionerCard(modifier: Modifier = Modifier, roomName: String = "房间
modifier = Modifier
.size(40.dp)
.clip(RoundedCornerShape(12.dp))
.background(Color(0x33FFFFFF))
.background(Color(0x55FFFFFF))
.clickable(enabled = isOn && temp < 32f) {
if (temp < 32f) temp += 1f
},
@ -777,10 +1105,10 @@ fun CustomSlider(
activeTrackColor = Brush.linearGradient(
listOf(Color(0xFF00D1FF), Color(0xFFA9F0FF))
).let { Color(0xFF00D1FF) },
inactiveTrackColor = Color(0x33FFFFFF),
inactiveTrackColor = Color(0x55FFFFFF),
disabledThumbColor = Color(0x66FFFFFF),
disabledActiveTrackColor = Color(0x33FFFFFF),
disabledInactiveTrackColor = Color(0x22FFFFFF)
disabledActiveTrackColor = Color(0x55FFFFFF),
disabledInactiveTrackColor = Color(0x55FFFFFF)
),
modifier = Modifier
.fillMaxWidth()
@ -806,7 +1134,7 @@ fun CustomSlider(
Box(
modifier = Modifier
.fillMaxSize()
.background(if (enabled) Color(0x33FFFFFF) else Color(0x22FFFFFF))
.background(if (enabled) Color(0x55FFFFFF) else Color(0x55FFFFFF))
)
// 激活轨道
Box(
@ -820,7 +1148,7 @@ fun CustomSlider(
)
} else {
Brush.linearGradient(
listOf(Color(0x33FFFFFF), Color(0x33FFFFFF))
listOf(Color(0x55FFFFFF), Color(0x55FFFFFF))
)
}
)
@ -843,9 +1171,9 @@ fun LightList(modifier: Modifier = Modifier) {
Column(modifier = modifier.fillMaxHeight(), verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text(text = "灯光", fontWeight = FontWeight.SemiBold)
lights.forEach { (name, percent) ->
Row(horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clip(RoundedCornerShape(16.dp)).background(Color(0x26121212)).padding(12.dp)) {
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) {
Box(modifier = Modifier.size(36.dp).clip(RoundedCornerShape(10.dp)).background(Color(0x22121212)))
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)
@ -965,6 +1293,14 @@ fun ModeButton(
val interaction = remember { MutableInteractionSource() }
var pressed by remember { mutableStateOf(false) }
// 根据文本确定图标
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(
@ -980,6 +1316,14 @@ fun ModeButton(
label = "elevation"
)
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) {
@ -995,38 +1339,25 @@ fun ModeButton(
modifier = modifier
.height(height)
.scale(scale)
.shadow(elevation, shape)
.background(
.clip(shape)
.then(
if (selected) {
brush
Modifier
.shadow(elevation, shape, clip = false)
.background(brush, shape)
.border(
width = 2.dp,
brush = Brush.linearGradient(
listOf(
Color(0x99FFFFFF),
Color(0x66FFFFFF)
)
),
shape = shape
)
} else {
Brush.linearGradient(
listOf(
Color(0x40FFFFFF),
Color(0x20FFFFFF)
)
)
},
shape = shape
)
.border(
width = if (selected) 2.dp else 1.dp,
brush = if (selected) {
Brush.linearGradient(
listOf(
Color(0x99FFFFFF),
Color(0x66FFFFFF)
)
)
} else {
Brush.linearGradient(
listOf(
Color(0x33FFFFFF),
Color(0x11FFFFFF)
)
)
},
shape = shape
Modifier.background(Color(0x50121212))
}
)
.clickable(indication = null, interactionSource = interaction) {
view.playSoundEffect(SoundEffectConstants.CLICK)
@ -1035,13 +1366,95 @@ fun ModeButton(
.padding(24.dp),
contentAlignment = Alignment.Center
) {
Text(
text = text,
color = if (selected) Color.White else Color(0xFFB0B0B0),
fontWeight = if (selected) FontWeight.Bold else FontWeight.Medium,
fontSize = if (selected) 22.sp else 20.sp,
style = MaterialTheme.typography.titleLarge
)
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
)
}
}
}
@ -1065,7 +1478,7 @@ fun LightRow(modifier: Modifier = Modifier, roomName: String = "房间") {
fun LightCard(name: String, percent: Float, modifier: Modifier = Modifier) {
var brightness by remember { mutableStateOf(percent) }
Box(modifier = modifier.height(100.dp).clip(RoundedCornerShape(16.dp)).background(Color(0x26121212)).padding(12.dp)) {
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) {
// 灯光图标 - 带发光效果
@ -1142,7 +1555,6 @@ fun AddRoomDialog(
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0x80000000))
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
@ -1176,8 +1588,8 @@ fun AddRoomDialog(
placeholder = { Text("请输入房间名称", color = Color(0xFF9AA0A6)) },
modifier = Modifier.fillMaxWidth(),
colors = androidx.compose.material3.TextFieldDefaults.colors(
focusedContainerColor = Color(0x22FFFFFF),
unfocusedContainerColor = Color(0x22FFFFFF),
focusedContainerColor = Color(0x55FFFFFF),
unfocusedContainerColor = Color(0x55FFFFFF),
focusedTextColor = Color.White,
unfocusedTextColor = Color.White,
cursorColor = Color(0xFFA9F0FF),
@ -1197,7 +1609,7 @@ fun AddRoomDialog(
.weight(1f)
.height(48.dp)
.clip(RoundedCornerShape(12.dp))
.background(Color(0x22FFFFFF))
.background(Color(0x55FFFFFF))
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }

View File

@ -130,7 +130,7 @@ fun SettingsTopBar(onBack: () -> Unit) {
modifier = Modifier
.size(40.dp)
.clip(RoundedCornerShape(12.dp))
.background(Color(0x22FFFFFF))
.background(Color(0x50121212))
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
@ -167,7 +167,7 @@ fun SettingsSection(title: String, content: @Composable ColumnScope.() -> Unit)
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(24.dp))
.background(Color(0x26FFFFFF))
.background(Color(0x50121212))
.padding(8.dp)
) {
Column(
@ -190,7 +190,7 @@ fun SettingsItem(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(16.dp))
.background(Color(0x11FFFFFF))
.background(Color(0x33121212))
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
@ -237,7 +237,7 @@ fun SettingsSwitchItem(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(16.dp))
.background(Color(0x11FFFFFF))
.background(Color(0x33121212))
.padding(16.dp)
) {
Row(
@ -312,13 +312,13 @@ fun LogoutButton(onClick: () -> Unit) {
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.shadow(8.dp, RoundedCornerShape(24.dp))
.clip(RoundedCornerShape(24.dp))
.background(
Brush.linearGradient(
listOf(Color(0xFFFF6B6B), Color(0xFFFF5252))
)
)
.shadow(8.dp, RoundedCornerShape(24.dp), clip = false)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }

View File

@ -1,3 +1,22 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="#121212"/>
</shape>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<!-- 渐变背景 -->
<path
android:pathData="M0,0 h108 v108 h-108 z">
<aapt:attr name="android:fillColor" xmlns:aapt="http://schemas.android.com/aapt">
<gradient
android:startY="0"
android:startX="0"
android:endY="108"
android:endX="108"
android:type="linear">
<item android:offset="0" android:color="#FF1A1A2E"/>
<item android:offset="1" android:color="#FF16213E"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@ -1,4 +1,46 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="108dp" android:height="108dp" android:viewportWidth="108" android:viewportHeight="108">
<path android:pathData="M24,54 L84,54" android:strokeColor="#FFFFFFFF" android:strokeWidth="6" android:fillColor="#00000000"/>
<path android:pathData="M54,24 L54,84" android:strokeColor="#FFFFFFFF" android:strokeWidth="6" android:fillColor="#00000000"/>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group
android:scaleX="0.08"
android:scaleY="0.08"
android:translateX="22"
android:translateY="22">
<!-- 中心圆 -->
<path
android:fillColor="#bfbfbf"
android:pathData="M512,540.44 m-312.89,0 a312.89,312.89 0,1,0 625.78,0 a312.89,312.89 0,1,0 -625.78,0 Z"/>
<!-- 中心环 -->
<path
android:fillColor="#FFFFFF"
android:pathData="M512,398.22 c227.56,0 412.44,56.89 412.44,128 S739.56,654.22 512,654.22 99.56,597.33 99.56,526.22 284.44,398.22 512,398.22 m0,-56.89 c-174.36,0 -469.33,38.97 -469.33,184.89 S337.64,711.11 512,711.11 s469.33,-38.97 469.33,-184.89 S686.36,341.33 512,341.33 z"/>
<!-- 左侧轨道 -->
<path
android:fillColor="#7dc5eb"
android:pathData="M312.89,117.48 a102.68,102.68 0,0,0 -56.89,16.5 c-36.12,23.89 -71.4,80.21 -32.71,213.9 a1077.48,1077.48 0,0,0 135.96,279.89 c93.01,142.22 241.78,307.2 354.99,307.2 a102.68,102.68 0,0,0 56.89,-16.5 c36.12,-23.89 71.4,-80.21 32.71,-213.9 a1077.48,1077.48 0,0,0 -135.96,-279.89 C573.44,284.44 424.68,117.48 312.89,117.48 z"/>
<!-- 右侧轨道 -->
<path
android:fillColor="#e89abe"
android:pathData="M695.18,106.38 c113.78,0 256,176.36 343.32,327.4 s200.82,426.67 74.52,498.92 a102.12,102.12 0,0,1 -51.48,13.37 c-113.78,0 -256,-176.36 -343.32,-327.4 s-200.82,-426.67 -74.52,-498.92 a102.12,102.12 0,0,1 51.48,-13.37 z"/>
<!-- 装饰点 -->
<path
android:fillColor="#FFFFFF"
android:pathData="M170.67,625.78 m-56.89,0 a56.89,56.89 0,1,0 113.78,0 a56.89,56.89 0,1,0 -113.78,0 Z"/>
<path
android:fillColor="#FFFFFF"
android:pathData="M597.33,170.67 m-56.89,0 a56.89,56.89 0,1,0 113.78,0 a56.89,56.89 0,1,0 -113.78,0 Z"/>
<path
android:fillColor="#FFFFFF"
android:pathData="M625.78,881.78 m-56.89,0 a56.89,56.89 0,1,0 113.78,0 a56.89,56.89 0,1,0 -113.78,0 Z"/>
</group>
</vector>

View File

@ -1,4 +0,0 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>