From 2c5afcfd6cf8d116f0ba2647f8c8e76582afd98b Mon Sep 17 00:00:00 2001 From: zzh Date: Thu, 27 Nov 2025 12:32:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=A9=E6=B0=94=E5=8A=A8?= =?UTF-8?q?=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/smarthome/ui/MainScaffold.kt | 316 ++++++++++++++---- 1 file changed, 255 insertions(+), 61 deletions(-) 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 48da923..9f0a6ae 100644 --- a/app/src/main/java/com/example/smarthome/ui/MainScaffold.kt +++ b/app/src/main/java/com/example/smarthome/ui/MainScaffold.kt @@ -3,71 +3,46 @@ 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.layout.* 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.material3.* +import androidx.compose.runtime.* +import androidx.compose.animation.core.* 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.geometry.Offset import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shadow 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 com.example.smarthome.R +import com.example.smarthome.data.WeatherInfo +import com.example.smarthome.data.WeatherService +import kotlinx.coroutines.launch +import kotlinx.coroutines.delay 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 -import com.example.smarthome.data.WeatherService -import com.example.smarthome.data.WeatherInfo @Composable fun MainScaffold( @@ -580,12 +555,13 @@ fun NavRailItem(text: String, iconRes: Int, selected: Boolean, onClick: () -> Un @Composable fun TopBar() { + val context = androidx.compose.ui.platform.LocalContext.current var weatherInfo by remember { mutableStateOf(WeatherService.getSimulatedWeather()) } var isLoading by remember { mutableStateOf(true) } LaunchedEffect(Unit) { try { - weatherInfo = WeatherService.getWeather() + weatherInfo = WeatherService.getWeather(context) } catch (e: Exception) { // 使用模拟数据 } @@ -604,28 +580,106 @@ fun TopBar() { @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(0xFFB8450A).copy(alpha = 0.55f), + Color(0xFFC06000).copy(alpha = 0.55f), + Color(0xFFB8450A).copy(alpha = 0.55f) + ) + // 雨天:深紫深蓝流动 + 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 backgroundBrush = Brush.linearGradient( + colors = colors, + start = Offset(offsetAnim, offsetAnim), + end = Offset(offsetAnim + 500f, offsetAnim + 500f) + ) + Box( modifier = Modifier .clip(RoundedCornerShape(16.dp)) - .background(Color(0x50121212)) - .padding(horizontal = 16.dp, vertical = 8.dp) + .background(backgroundBrush) // 使用动态渐变背景 ) { + // 天气特效层(在背景之上,内容之下)- 使用固定大小 + WeatherEffectLayer( + weather = weatherInfo.weather, + modifier = Modifier.size(width = 300.dp, height = 60.dp) + ) + Row( - horizontalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.padding(horizontal = 20.dp, vertical = 12.dp), + horizontalArrangement = Arrangement.spacedBy(20.dp), verticalAlignment = Alignment.CenterVertically ) { // 温度和天气 Column(horizontalAlignment = Alignment.CenterHorizontally) { Text( text = if (isLoading) "--" else "${weatherInfo.temperature}°C", - fontSize = 18.sp, + fontSize = 22.sp, fontWeight = FontWeight.Bold, - color = Color.White + 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 = 12.sp, - color = Color(0xFF9AA0A6) + 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) + ) ) } @@ -633,22 +687,28 @@ fun WeatherCard(weatherInfo: WeatherInfo, isLoading: Boolean) { Box( modifier = Modifier .width(1.dp) - .height(30.dp) - .background(Color(0x33FFFFFF)) + .height(36.dp) + .background(Color.White.copy(alpha = 0.3f)) ) // 湿度 Column(horizontalAlignment = Alignment.CenterHorizontally) { Text( text = if (isLoading) "--" else "${weatherInfo.humidity}%", - fontSize = 14.sp, - fontWeight = FontWeight.Medium, - color = Color.White + 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 = 11.sp, - color = Color(0xFF9AA0A6) + 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) + ) ) } @@ -656,36 +716,46 @@ fun WeatherCard(weatherInfo: WeatherInfo, isLoading: Boolean) { Box( modifier = Modifier .width(1.dp) - .height(30.dp) - .background(Color(0x33FFFFFF)) + .height(36.dp) + .background(Color.White.copy(alpha = 0.3f)) ) // 空气质量 Column(horizontalAlignment = Alignment.CenterHorizontally) { val aqiColor = when { - weatherInfo.aqi <= 50 -> Color(0xFF00E676) + 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 = 14.sp, - fontWeight = FontWeight.Medium, - color = aqiColor + 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 = 11.sp, - color = Color(0xFF9AA0A6) + 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) + ) ) } // 城市 Text( text = weatherInfo.city, - fontSize = 12.sp, - color = Color(0xFFB0B0B0) + fontSize = 15.sp, + fontWeight = FontWeight.Medium, + color = Color.White, + style = androidx.compose.ui.text.TextStyle( + shadow = Shadow(color = Color.Black.copy(alpha = 0.3f), blurRadius = 4f) + ) ) } } @@ -1544,6 +1614,130 @@ fun LightCard(name: String, percent: Float, modifier: Modifier = Modifier) { } } +@Composable +fun WeatherEffectLayer(weather: String, modifier: Modifier = Modifier) { + val infiniteTransition = rememberInfiniteTransition(label = "weather_effect") + + // 太阳旋转动画 + val sunRotation by infiniteTransition.animateFloat( + initialValue = 0f, + targetValue = 360f, + animationSpec = infiniteRepeatable( + animation = tween(20000, easing = LinearEasing) + ), + label = "sun_rotation" + ) + + // 雨雪下落动画 + val dropOffset by infiniteTransition.animateFloat( + initialValue = 0f, + targetValue = 1f, + animationSpec = infiniteRepeatable( + animation = tween(1500, easing = LinearEasing) + ), + label = "drop_offset" + ) + + // 云朵飘动动画 + val cloudOffset by infiniteTransition.animateFloat( + initialValue = -20f, + targetValue = 20f, + animationSpec = infiniteRepeatable( + animation = tween(5000, easing = LinearEasing), + repeatMode = RepeatMode.Reverse + ), + label = "cloud_offset" + ) + + androidx.compose.foundation.Canvas(modifier = modifier) { + val width = size.width + val height = size.height + + when { + // 晴天:绘制旋转的太阳 + weather.contains("晴") -> { + // 太阳核心 + val sunCenterX = width - 25.dp.toPx() + val sunCenterY = 25.dp.toPx() + + drawCircle( + color = Color(0xFFFFD700).copy(alpha = 0.6f), + radius = 18.dp.toPx(), + center = Offset(sunCenterX, sunCenterY) + ) + + // 太阳光芒 - 使用数学计算位置 + val rayLength = 10.dp.toPx() + val rayStart = 20.dp.toPx() + + for (i in 0 until 8) { + val angle = Math.toRadians((sunRotation + i * 45f).toDouble()) + val startX = sunCenterX + kotlin.math.cos(angle).toFloat() * rayStart + val startY = sunCenterY + kotlin.math.sin(angle).toFloat() * rayStart + val endX = sunCenterX + kotlin.math.cos(angle).toFloat() * (rayStart + rayLength) + val endY = sunCenterY + kotlin.math.sin(angle).toFloat() * (rayStart + rayLength) + + drawLine( + color = Color(0xFFFFD700).copy(alpha = 0.5f), + start = Offset(startX, startY), + end = Offset(endX, endY), + strokeWidth = 2.dp.toPx() + ) + } + } + + // 雨天:绘制下落的雨滴 + weather.contains("雨") -> { + val dropCount = 8 + val dropLength = 8.dp.toPx() + + for (i in 0 until dropCount) { + val x = (i * width / dropCount) + (i * 17 % 30) + val startY = (i * 29 % height.toInt()).toFloat() + val currentY = (startY + dropOffset * height) % height + + drawLine( + color = Color.White.copy(alpha = 0.3f), + start = Offset(x, currentY), + end = Offset(x - 2.dp.toPx(), currentY + dropLength), + strokeWidth = 1.5f.dp.toPx() + ) + } + } + + // 雪天:绘制飘落的雪花(圆点) + weather.contains("雪") -> { + val snowCount = 8 + + for (i in 0 until snowCount) { + val x = (i * width / snowCount) + (i * 23 % 40) + val startY = (i * 41 % height.toInt()).toFloat() + val currentY = (startY + dropOffset * height * 0.5f) % height + + drawCircle( + color = Color.White.copy(alpha = 0.5f), + radius = (1.5f + i % 2).dp.toPx(), + center = Offset(x, currentY) + ) + } + } + + // 多云/阴天:绘制云朵 + weather.contains("云") || weather.contains("阴") -> { + // 绘制几个叠加的圆组成云 - 缩小尺寸 + val cloudColor = Color.White.copy(alpha = 0.15f) + val baseX = width - 35.dp.toPx() + cloudOffset * 0.5f + val baseY = 25.dp.toPx() + + drawCircle(color = cloudColor, radius = 15.dp.toPx(), center = Offset(baseX, baseY)) + drawCircle(color = cloudColor, radius = 12.dp.toPx(), center = Offset(baseX - 18.dp.toPx(), baseY + 5.dp.toPx())) + drawCircle(color = cloudColor, radius = 14.dp.toPx(), center = Offset(baseX + 15.dp.toPx(), baseY + 3.dp.toPx())) + drawCircle(color = cloudColor, radius = 10.dp.toPx(), center = Offset(baseX - 8.dp.toPx(), baseY - 8.dp.toPx())) + } + } + } +} + @Composable fun AddRoomDialog(