diff --git a/app/src/main/java/com/example/smarthome/MainActivity.kt b/app/src/main/java/com/example/smarthome/MainActivity.kt index 91c3caa..4a501c8 100644 --- a/app/src/main/java/com/example/smarthome/MainActivity.kt +++ b/app/src/main/java/com/example/smarthome/MainActivity.kt @@ -1,6 +1,5 @@ package com.example.smarthome -import android.graphics.drawable.Drawable import android.os.Bundle import androidx.activity.ComponentActivity import com.example.smarthome.ui.theme.SmartHomeTheme @@ -54,35 +53,22 @@ class MainActivity : ComponentActivity() { } private fun hideStatusBar() { - // 设置全屏模式,让内容延伸到系统栏区域 + // 让内容延伸到系统栏区域 WindowCompat.setDecorFitsSystemWindows(window, false) - - // 设置全屏标志 - window.setFlags( - android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN, - android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN - ) - - // 设置状态栏和导航栏为透明 - window.statusBarColor = android.graphics.Color.TRANSPARENT - window.navigationBarColor = android.graphics.Color.TRANSPARENT - - // 隐藏状态栏和导航栏 - window.decorView.systemUiVisibility = ( - android.view.View.SYSTEM_UI_FLAG_FULLSCREEN - or android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - or android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE - or android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - or android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - ) - - // 支持刘海屏设备 - val lp = window.attributes - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) { - lp.layoutInDisplayCutoutMode = android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES + + // 使用现代 WindowInsetsController API 隐藏状态栏和导航栏 + androidx.core.view.WindowInsetsControllerCompat(window, window.decorView).apply { + hide(androidx.core.view.WindowInsetsCompat.Type.statusBars()) + hide(androidx.core.view.WindowInsetsCompat.Type.navigationBars()) + systemBarsBehavior = androidx.core.view.WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + } + + // 支持刘海屏设备 + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) { + window.attributes = window.attributes.also { + it.layoutInDisplayCutoutMode = android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES + } } - window.attributes = lp } } @@ -134,10 +120,8 @@ fun MainContent() { rooms = roomList } - // 初始化背景管理器并获取当前选中的背景 - androidx.compose.runtime.LaunchedEffect(Unit) { - BackgroundManager.init(context) - } + // 初始化背景管理器并获取当前选中的背景(同步初始化,避免首帧闪烁默认背景) + remember { BackgroundManager.init(context) } val selectedBgId by BackgroundManager.selectedBackground.collectAsState() val currentBg = BackgroundManager.backgrounds.getOrNull(selectedBgId) val backgroundRes = currentBg?.resourceId ?: R.drawable.background1 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 4025617..c301ad3 100644 --- a/app/src/main/java/com/example/smarthome/ui/MainScaffold.kt +++ b/app/src/main/java/com/example/smarthome/ui/MainScaffold.kt @@ -10,7 +10,6 @@ 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.* import androidx.compose.runtime.* import androidx.compose.animation.core.* @@ -20,13 +19,10 @@ 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.draw.blur -import androidx.compose.ui.draw.drawBehind 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.graphics.drawscope.DrawScope import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -71,8 +67,6 @@ import android.content.ContextWrapper import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.HazeStyle import dev.chrisbanes.haze.HazeTint -import dev.chrisbanes.haze.haze -import dev.chrisbanes.haze.hazeChild import dev.chrisbanes.haze.hazeSource import dev.chrisbanes.haze.hazeEffect @@ -93,6 +87,12 @@ fun MainScaffold( // Haze state for blur effects val hazeState = remember { HazeState() } + // 读取当前选中的背景壁纸 + val context = androidx.compose.ui.platform.LocalContext.current + remember { BackgroundManager.init(context) } + val selectedBgId by BackgroundManager.selectedBackground.collectAsState() + val backgroundRes = BackgroundManager.backgrounds.getOrNull(selectedBgId)?.resourceId ?: R.drawable.background1 + Box(modifier = Modifier.fillMaxSize()) { // 背景作为模糊源 Box( @@ -102,7 +102,7 @@ fun MainScaffold( ) { // 背景图片 Image( - painter = painterResource(id = R.drawable.background), + painter = painterResource(id = backgroundRes), contentDescription = null, modifier = Modifier.fillMaxSize(), contentScale = ContentScale.Crop, @@ -181,7 +181,7 @@ fun MainScaffold( } } } - } +} } @Composable @@ -349,11 +349,97 @@ fun AllDevicesOverview(modifier: Modifier = Modifier) { Device("影音室电视", "运行中", R.drawable.ic_media, true), Device("游戏房主机", "待机中", R.drawable.ic_media, false) ) + val rows = allDevices.chunked(2) Column( modifier = modifier, - verticalArrangement = Arrangement.spacedBy(8.dp) + verticalArrangement = Arrangement.spacedBy(12.dp) ) { - allDevices.forEach { d -> DeviceCard(d) } + rows.forEach { rowDevices -> + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + rowDevices.forEach { d -> + DeviceSquareCard(d, modifier = Modifier.weight(1f)) + } + if (rowDevices.size < 2) { + Spacer(modifier = Modifier.weight(1f)) + } + } + } + } +} + +@Composable +fun DeviceSquareCard(d: Device, modifier: Modifier = Modifier) { + var active by remember { mutableStateOf(d.on) } + val accentColor = if (active) Color(0xFF00D1FF) else Color(0xFF5A5A6E) + val bgAlpha = if (active) 0.75f else 0.45f + + Box( + modifier = modifier + .aspectRatio(1f) + .clip(RoundedCornerShape(20.dp)) + .background(Color(0xFF1A1A1A).copy(alpha = bgAlpha)) + .border( + width = 1.dp, + color = if (active) accentColor.copy(alpha = 0.4f) else Color.White.copy(alpha = 0.12f), + shape = RoundedCornerShape(20.dp) + ) + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { active = !active } + .padding(16.dp) + ) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.SpaceBetween + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.Top + ) { + Box( + modifier = Modifier + .size(44.dp) + .clip(RoundedCornerShape(12.dp)) + .background(accentColor.copy(alpha = if (active) 0.2f else 0.1f)), + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource(id = d.icon), + contentDescription = null, + modifier = Modifier.size(26.dp), + colorFilter = androidx.compose.ui.graphics.ColorFilter.tint( + if (active) accentColor else Color(0xFF6A6A7E) + ) + ) + } + Box( + modifier = Modifier + .size(8.dp) + .clip(RoundedCornerShape(4.dp)) + .background(if (active) Color(0xFF00E676) else Color(0xFF4A4A5E)) + ) + } + Column(verticalArrangement = Arrangement.spacedBy(4.dp)) { + Text( + text = d.name, + color = Color.White, + fontSize = 14.sp, + fontWeight = FontWeight.SemiBold, + maxLines = 1 + ) + Text( + text = d.sub, + color = if (active) accentColor else Color(0xFF6A6A7E), + fontSize = 12.sp, + maxLines = 1 + ) + } + } } } @@ -401,24 +487,16 @@ fun QuickActions() { @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), @@ -705,69 +783,41 @@ fun FloatingBottomNav( R.drawable.ic_security, R.drawable.ic_settings ) - + val itemWidth = 52.dp val itemSpacing = 4.dp - + // 是否正在拖动 var isDragging by remember { mutableStateOf(false) } // 拖动时的临时选中项 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) { dragIndex = selectedNavItem } } - - // 选中框的动画偏移量 - 跟随 dragIndex(拖动时)或 selectedNavItem(点击时) + + // 选中指示器偏移 - 拖动时 snap 跟随手指,松手后快速回弹 val currentIndex = if (isDragging) dragIndex else selectedNavItem val animatedOffset by animateFloatAsState( targetValue = currentIndex * (itemWidth.value + itemSpacing.value), - animationSpec = spring( - dampingRatio = Spring.DampingRatioMediumBouncy, - stiffness = Spring.StiffnessHigh + animationSpec = if (isDragging) snap() else tween( + durationMillis = 180, + easing = androidx.compose.animation.core.FastOutSlowInEasing ), - label = "liquid_glass_offset" + label = "nav_indicator_offset" ) - + // 菜单栏高度(胶囊形) val navBarHeight = 68.dp val navBarPadding = 10.dp val innerHeight = navBarHeight - navBarPadding * 2 // 48dp - - // 选中指示器尺寸 - 按住时放大到贴合菜单栏边缘 - val indicatorSize by animateFloatAsState( - targetValue = if (isDragging) (navBarHeight.value - 4f) else innerHeight.value, - animationSpec = spring( - dampingRatio = Spring.DampingRatioMediumBouncy, - stiffness = Spring.StiffnessMedium - ), - label = "indicator_size" - ) - + Box( modifier = modifier .height(navBarHeight) - .alpha(navBarAlpha) .shadow(24.dp, RoundedCornerShape(50)) .clip(RoundedCornerShape(50)) .background( @@ -792,9 +842,6 @@ 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) @@ -802,12 +849,10 @@ fun FloatingBottomNav( }, onDragEnd = { isDragging = false - lastInteractionTime = System.currentTimeMillis() onNavItemSelect(dragIndex) }, onDragCancel = { isDragging = false - lastInteractionTime = System.currentTimeMillis() dragIndex = selectedNavItem }, onHorizontalDrag = { change, _ -> @@ -824,9 +869,10 @@ fun FloatingBottomNav( Box( contentAlignment = Alignment.CenterStart ) { - // 选中指示器 - 按住时放大贴合菜单栏边缘 + // 选中指示器 + val indicatorSize = innerHeight.value val horizontalOffset = (itemWidth.value - indicatorSize) / 2f - + Box( modifier = Modifier .align(Alignment.CenterStart) @@ -861,12 +907,7 @@ fun FloatingBottomNav( indication = null, interactionSource = remember { MutableInteractionSource() } ) { - if (!isDragging) { - // 重置空闲状态 - isIdle = false - lastInteractionTime = System.currentTimeMillis() - onNavItemSelect(index) - } + onNavItemSelect(index) }, contentAlignment = Alignment.Center ) { @@ -962,18 +1003,11 @@ fun ThemeModeToggle(isDarkMode: Boolean, onToggle: () -> Unit) { @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 .width(80.dp) // 固定宽度,防止文字长度不一致导致图标不对齐 - .scale(scale) .clickable( indication = null, interactionSource = remember { MutableInteractionSource() } @@ -1182,20 +1216,20 @@ fun WeatherCard(weatherInfo: WeatherInfo, isLoading: Boolean) { color = accentColor.copy(alpha = 0.2f), shape = RoundedCornerShape(16.dp) ) - .padding(start = 4.dp, top = 4.dp, bottom = 4.dp, end = 20.dp), + .padding(start = 4.dp, top = 2.dp, bottom = 2.dp, end = 12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically ) { // 天气图标(带阴影的渐变背景) Box( modifier = Modifier - .size(48.dp) + .size(30.dp) .shadow( - elevation = 8.dp, - shape = RoundedCornerShape(14.dp), + elevation = 4.dp, + shape = RoundedCornerShape(8.dp), spotColor = weatherColors.second.copy(alpha = 0.3f) ) - .clip(RoundedCornerShape(14.dp)) + .clip(RoundedCornerShape(8.dp)) .background( Brush.linearGradient( colors = listOf(weatherColors.first, weatherColors.second), @@ -1215,74 +1249,37 @@ fun WeatherCard(weatherInfo: WeatherInfo, isLoading: Boolean) { else -> R.drawable.ic_weather_cloudy }), contentDescription = "天气", - modifier = Modifier.size(24.dp), + modifier = Modifier.size(18.dp), colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(Color.White) ) } // 温度和天气状况 - Column { + Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { Text( text = if (isLoading) "--°C" else "${weatherInfo.temperature}°C", - fontSize = 22.sp, + fontSize = 15.sp, fontWeight = FontWeight.Bold, color = Color.White, - lineHeight = 22.sp + lineHeight = 15.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) + horizontalArrangement = Arrangement.spacedBy(4.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}%", + text = if (isLoading) "加载中" else weatherInfo.weather, 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) + color = accentColor.copy(alpha = 0.9f) ) + if (!isLoading && weatherInfo.city.isNotEmpty() && weatherInfo.city != "未知") { + Text(text = "·", fontSize = 11.sp, color = Color.White.copy(alpha = 0.3f)) + Text( + text = weatherInfo.city, + fontSize = 11.sp, + color = Color.White.copy(alpha = 0.55f) + ) + } } } } @@ -1400,21 +1397,14 @@ fun UsageStatusChart(modifier: Modifier = Modifier, roomName: String = "房间", // 主题色 val accentColor = Color(0xFF00D1FF) - + Box( modifier = modifier .clip(RoundedCornerShape(24.dp)) - .hazeEffect( - state = hazeState, - style = HazeStyle( - blurRadius = 40.dp, - tint = HazeTint(Color.White.copy(alpha = 0.15f)), - noiseFactor = 0.0f - ) - ) + .background(Color(0xFF1A1A1A).copy(alpha = 0.55f)) .border( width = 1.dp, - color = Color.White.copy(alpha = 0.3f), + color = Color.White.copy(alpha = 0.15f), shape = RoundedCornerShape(24.dp) ) .padding(20.dp) @@ -1535,7 +1525,7 @@ fun EnergyTrendChart() { val prevX = (index - 1) * stepX val prevY = canvasHeight - (dataPoints[index - 1] * canvasHeight) val controlX = (prevX + x) / 2 - quadraticBezierTo(controlX, prevY, x, y) + quadraticTo(controlX, prevY, x, y) } } @@ -1565,7 +1555,7 @@ fun EnergyTrendChart() { val prevX = (index - 1) * stepX val prevY = canvasHeight - (dataPoints[index - 1] * canvasHeight) val controlX = (prevX + x) / 2 - quadraticBezierTo(controlX, prevY, x, y) + quadraticTo(controlX, prevY, x, y) } } } @@ -1610,21 +1600,14 @@ fun AirConditionerCard( val pm25 = remember { mutableStateOf(35) } val co2 = remember { mutableStateOf(420) } - // 外层大卡片 - 使用 Haze 毛玻璃效果 + // 外层大卡片 - 使用半透明背景替代实时模糊,降低滚动时 GPU 开销 Box( modifier = modifier .clip(RoundedCornerShape(24.dp)) - .hazeEffect( - state = hazeState, - style = HazeStyle( - blurRadius = 40.dp, - tint = HazeTint(Color.White.copy(alpha = 0.15f)), - noiseFactor = 0.0f - ) - ) + .background(Color(0xFF1A1A1A).copy(alpha = 0.55f)) .border( width = 1.dp, - color = Color.White.copy(alpha = 0.3f), + color = Color.White.copy(alpha = 0.15f), shape = RoundedCornerShape(24.dp) ) .padding(20.dp) @@ -2023,9 +2006,7 @@ fun ModeButton( onClick: () -> Unit ) { val shape = RoundedCornerShape(24.dp) - val interaction = remember { MutableInteractionSource() } - var pressed by remember { mutableStateOf(false) } - + // 根据文本确定图标 val iconRes = when (text) { "回家" -> R.drawable.ic_mode_home @@ -2034,41 +2015,17 @@ fun ModeButton( "观影" -> R.drawable.ic_media 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" - ) - - 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 - } - } - } + val view = LocalView.current + val interaction = remember { MutableInteractionSource() } Box( modifier = modifier - .scale(scale) .clip(shape) .then( if (selected) { Modifier - .shadow(elevation, shape, clip = false) + .shadow(12.dp, shape, clip = false) .background(Color.White, shape) .border( width = 1.dp, @@ -2312,130 +2269,41 @@ fun LightSlider( @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(8000, easing = LinearEasing), - repeatMode = RepeatMode.Restart - ), - 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(), + color = Color(0xFFFFD700).copy(alpha = 0.5f), + radius = 16.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 = 20 - val dropLength = 10.dp.toPx() - - for (i in 0 until dropCount) { - // 使用不同的起始位置和速度让雨滴错开 + weather.contains("雨") || weather.contains("雪") -> { + // 静态雨滴/雪花装饰点 + val count = if (weather.contains("雪")) 8 else 12 + repeat(count) { i -> val xOffset = (i * 37 % 100) / 100f val x = width * xOffset - val speedFactor = 3f + (i % 5) * 0.5f // 更快的速度,一个周期内多次循环 - val startOffset = (i * 73 % 100) / 100f // 不同起始位置 - val currentY = ((startOffset + dropOffset * speedFactor) % 1f) * height - - drawLine( - color = Color.White.copy(alpha = 0.35f), - start = Offset(x, currentY), - end = Offset(x, currentY + dropLength), // 垂直下落 - strokeWidth = 1.5f.dp.toPx() - ) - } - } - - // 雪天:绘制飘落的雪花(圆点) - weather.contains("雪") -> { - val snowCount = 18 - - for (i in 0 until snowCount) { - // 使用不同的起始位置和速度让雪花错开 - val xOffset = (i * 41 % 100) / 100f - val x = width * xOffset - val speedFactor = 1.5f + (i % 4) * 0.3f // 雪花下落更慢 - val startOffset = (i * 67 % 100) / 100f - val currentY = ((startOffset + dropOffset * speedFactor) % 1f) * height - + val y = 12.dp.toPx() + (i % 4) * 10.dp.toPx() drawCircle( - color = Color.White.copy(alpha = 0.5f), - radius = (1.5f + i % 2).dp.toPx(), - center = Offset(x, currentY) + color = Color.White.copy(alpha = 0.25f), + radius = (if (weather.contains("雪")) 1.5f else 1f).dp.toPx(), + center = Offset(x, y) ) } } - - // 多云/阴天:绘制云朵 weather.contains("云") || weather.contains("阴") -> { - // 绘制几个叠加的圆组成云 - 缩小尺寸 - val cloudColor = Color.White.copy(alpha = 0.15f) - val baseX = width - 35.dp.toPx() + cloudOffset * 0.5f + val cloudColor = Color.White.copy(alpha = 0.12f) + val baseX = width - 35.dp.toPx() 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())) + drawCircle(color = cloudColor, radius = 14.dp.toPx(), center = Offset(baseX, baseY)) + drawCircle(color = cloudColor, radius = 11.dp.toPx(), center = Offset(baseX - 16.dp.toPx(), baseY + 4.dp.toPx())) + drawCircle(color = cloudColor, radius = 12.dp.toPx(), center = Offset(baseX + 13.dp.toPx(), baseY + 2.dp.toPx())) } } } @@ -2445,7 +2313,7 @@ fun WeatherEffectLayer(weather: String, modifier: Modifier = Modifier) { @Composable fun BlurGlassCard( modifier: Modifier = Modifier, - blurRadius: Float = 25f, + blurRadius: Float = 16f, overlayColor: String = "#40FFFFFF", content: @Composable () -> Unit ) { @@ -2513,8 +2381,8 @@ fun GlassSidebar( .hazeEffect( state = hazeState, style = HazeStyle( - blurRadius = 40.dp, - tint = HazeTint(Color.White.copy(alpha = 0.15f)), + blurRadius = 16.dp, + tint = HazeTint(Color.White.copy(alpha = 0.12f)), noiseFactor = 0.0f ) ) @@ -2542,14 +2410,14 @@ fun GlassSidebar( // 品牌标题 Column { Text( - text = "HEMS", + text = "TZ", color = Color.White, fontSize = 32.sp, fontWeight = FontWeight.Bold, letterSpacing = 1.sp ) Text( - text = "Smart Home", + text = "Energy-saving system", color = Color.White.copy(alpha = 0.6f), fontSize = 14.sp, letterSpacing = 0.5.sp @@ -2665,6 +2533,7 @@ fun GlassSidebar( } } } +} // 扩展函数用于获取 Activity fun Context.findActivity(): Activity { @@ -2675,4 +2544,3 @@ fun Context.findActivity(): Activity { } throw IllegalStateException("Permissions should be called in the context of an Activity") } -} diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index d3dee3d..0b5d710 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,6 +2,6 @@ - + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index d3dee3d..0b5d710 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -2,6 +2,6 @@ - + diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index 7a83a02..82b70d8 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index 7a83a02..82b70d8 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png index 818b1c3..63e352b 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png index 818b1c3..63e352b 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index fad471c..6a06973 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png index fad471c..6a06973 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index aa2ae45..9aabe91 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index aa2ae45..9aabe91 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index ac85b89..991c108 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index ac85b89..991c108 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ