优化细节

This commit is contained in:
zzh 2025-11-27 16:00:44 +08:00
parent a9f61e0663
commit ae9723964e
8 changed files with 328 additions and 80 deletions

View File

@ -26,6 +26,8 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.runtime.collectAsState
import com.example.smarthome.data.BackgroundManager
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -113,10 +115,20 @@ fun AppRoot() {
rooms = roomList rooms = roomList
} }
// 初始化背景管理器并获取当前选中的背景
androidx.compose.runtime.LaunchedEffect(Unit) {
BackgroundManager.init(context)
}
val selectedBgId by BackgroundManager.selectedBackground.collectAsState()
val currentBg = BackgroundManager.backgrounds.getOrNull(selectedBgId)
val backgroundRes = currentBg?.resourceId ?: R.drawable.background1
// 根据背景类型调整遮罩透明度:暗色背景减少遮罩,亮色背景增加遮罩
val overlayAlpha = if (currentBg?.isDark == true) 0x11 else 0x88
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
// 背景图片 - 填充整个屏幕包括系统栏使用FillBounds确保完全填充 // 背景图片 - 填充整个屏幕包括系统栏使用FillBounds确保完全填充
Image( Image(
painter = painterResource(id = R.drawable.background), painter = painterResource(id = backgroundRes),
contentDescription = null, contentDescription = null,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
@ -127,7 +139,7 @@ fun AppRoot() {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(Color(0x88121212)) .background(Color(overlayAlpha shl 24 or 0x121212))
) )
// 主内容 - 添加系统栏内边距 // 主内容 - 添加系统栏内边距

View File

@ -26,6 +26,11 @@ data class WeatherInfo(
object WeatherService { object WeatherService {
private const val TAG = "WeatherService" private const val TAG = "WeatherService"
// 缓存天气数据,避免重复获取
private var cachedWeather: WeatherInfo? = null
private var lastFetchTime: Long = 0
private const val CACHE_DURATION = 10 * 60 * 1000L // 10分钟缓存
private val client = OkHttpClient.Builder() private val client = OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS) .connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS) .readTimeout(15, TimeUnit.SECONDS)
@ -34,8 +39,16 @@ object WeatherService {
/** /**
* 获取天气信息 * 获取天气信息
* 优先使用GPS定位失败则用IP定位 * 优先使用GPS定位失败则用IP定位
* 带缓存机制10分钟内不重复获取
*/ */
suspend fun getWeather(context: Context): WeatherInfo { suspend fun getWeather(context: Context, forceRefresh: Boolean = false): WeatherInfo {
// 检查缓存
val now = System.currentTimeMillis()
if (!forceRefresh && cachedWeather != null && (now - lastFetchTime) < CACHE_DURATION) {
Log.d(TAG, "Using cached weather data")
return cachedWeather!!
}
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
// 1. 优先尝试GPS定位 // 1. 优先尝试GPS定位
val gpsLocation = getGpsLocation(context) val gpsLocation = getGpsLocation(context)
@ -46,6 +59,8 @@ object WeatherService {
// 用GPS坐标获取天气 // 用GPS坐标获取天气
val weatherResult = getWeatherByLocation(lat, lon, city) val weatherResult = getWeatherByLocation(lat, lon, city)
if (weatherResult != null) { if (weatherResult != null) {
cachedWeather = weatherResult
lastFetchTime = now
return@withContext weatherResult return@withContext weatherResult
} }
} }
@ -54,6 +69,8 @@ object WeatherService {
Log.w(TAG, "GPS failed, trying wttr.in...") Log.w(TAG, "GPS failed, trying wttr.in...")
val wttrResult = tryWttrIn() val wttrResult = tryWttrIn()
if (wttrResult != null) { if (wttrResult != null) {
cachedWeather = wttrResult
lastFetchTime = now
return@withContext wttrResult return@withContext wttrResult
} }
@ -61,6 +78,8 @@ object WeatherService {
Log.w(TAG, "wttr.in failed, trying backup...") Log.w(TAG, "wttr.in failed, trying backup...")
val backupResult = tryBackupApi() val backupResult = tryBackupApi()
if (backupResult != null) { if (backupResult != null) {
cachedWeather = backupResult
lastFetchTime = now
return@withContext backupResult return@withContext backupResult
} }

View File

@ -316,7 +316,7 @@ fun RoomTabs(
} }
// 固定的添加按钮 // 固定的添加按钮
GradientButton("+ 添加", onClick = onAddRoomClick) GradientButton("+ 添加房间", onClick = onAddRoomClick)
} }
} }
@ -490,17 +490,82 @@ fun NavItem(title: String, selected: Boolean) {
@Composable @Composable
fun SideNavRail(selectedNavItem: Int = 0, onNavItemSelect: (Int) -> Unit = {}) { fun SideNavRail(selectedNavItem: Int = 0, onNavItemSelect: (Int) -> Unit = {}) {
Column(modifier = Modifier.width(100.dp).fillMaxHeight().padding(start = 12.dp, end = 4.dp, top = 12.dp, bottom = 12.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { val context = androidx.compose.ui.platform.LocalContext.current
Spacer(modifier = Modifier.height(48.dp)) val selectedBg by com.example.smarthome.data.BackgroundManager.selectedBackground.collectAsState()
NavRailItem("控制台", R.drawable.ic_dashboard, selectedNavItem == 0, onClick = { onNavItemSelect(0) }) val isDarkMode = com.example.smarthome.data.BackgroundManager.backgrounds.getOrNull(selectedBg)?.isDark ?: false
NavRailItem("场景", R.drawable.ic_scene, selectedNavItem == 1, onClick = { onNavItemSelect(1) })
NavRailItem("自动化", R.drawable.ic_automation, selectedNavItem == 2, onClick = { onNavItemSelect(2) }) Column(
NavRailItem("统计", R.drawable.ic_statistics, selectedNavItem == 3, onClick = { onNavItemSelect(3) }) modifier = Modifier
NavRailItem("安全", R.drawable.ic_security, selectedNavItem == 4, onClick = { onNavItemSelect(4) }) .width(100.dp)
NavRailItem("设置", R.drawable.ic_settings, selectedNavItem == 5, onClick = { onNavItemSelect(5) }) .fillMaxHeight()
.padding(start = 12.dp, end = 4.dp, top = 12.dp, bottom = 12.dp),
verticalArrangement = Arrangement.SpaceBetween
) {
// 上半部分:导航项
Column(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_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) })
}
// 底部:模式切换按钮(暂时注释)
/*
ThemeModeToggle(
isDarkMode = isDarkMode,
onToggle = {
if (isDarkMode) {
com.example.smarthome.data.BackgroundManager.setBackground(context, 0) // 丝绸白
} else {
com.example.smarthome.data.BackgroundManager.setBackground(context, 2) // 紫金绸
}
}
)
*/
} }
} }
/*
@Composable
fun ThemeModeToggle(isDarkMode: Boolean, onToggle: () -> Unit) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) { onToggle() }
.padding(vertical = 4.dp)
) {
Box(
modifier = Modifier
.size(48.dp)
.clip(RoundedCornerShape(14.dp))
.background(
if (isDarkMode) {
Brush.linearGradient(
listOf(Color(0xFF2C2C54), Color(0xFF1A1A2E))
)
} else {
Brush.linearGradient(
listOf(Color(0xFFFFE082), Color(0xFFFFD54F))
)
}
),
contentAlignment = Alignment.Center
) {
Text(
text = if (isDarkMode) "🌙" else "☀️",
fontSize = 20.sp
)
}
}
}
*/
@Composable @Composable
fun NavRailItem(text: String, iconRes: Int, selected: Boolean, onClick: () -> Unit = {}) { fun NavRailItem(text: String, iconRes: Int, selected: Boolean, onClick: () -> Unit = {}) {
val scale by androidx.compose.animation.core.animateFloatAsState( val scale by androidx.compose.animation.core.animateFloatAsState(
@ -574,11 +639,7 @@ fun TopBar() {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) {
Text(text = "控制台", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = Color.White) Text(text = "控制台", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = Color.White)
Row(horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically) { WeatherCard(weatherInfo = weatherInfo, isLoading = isLoading)
// 天气信息卡片
WeatherCard(weatherInfo = weatherInfo, isLoading = isLoading)
GradientButton("+ 添加设备")
}
} }
} }
@ -1060,9 +1121,40 @@ fun AirConditionerCard(modifier: Modifier = Modifier, roomName: String = "房间
var selectedMode by remember { mutableStateOf(0) } // 0:制冷 1:制热 2:除湿 3:送风 var selectedMode by remember { mutableStateOf(0) } // 0:制冷 1:制热 2:除湿 3:送风
var fanSpeed by remember { mutableStateOf(1) } // 0:自动 1:低 2:中 3:高 var fanSpeed by remember { mutableStateOf(1) } // 0:自动 1:低 2:中 3:高
val modes = listOf("❄️" to "制冷", "🔥" to "制热", "💧" to "除湿", "🌀" to "送风") // 模式emoji图标
val modeIcons = listOf(
"❄️" to "制冷",
"☀️" to "制热",
"💧" to "除湿",
"🌀" to "送风"
)
val fanSpeeds = listOf("自动", "", "", "") val fanSpeeds = listOf("自动", "", "", "")
// 图标动画
val infiniteTransition = rememberInfiniteTransition(label = "ac_icon")
// 旋转动画(制冷、制热、送风)
val iconRotation by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 3000, easing = LinearEasing),
repeatMode = RepeatMode.Restart
),
label = "icon_rotation"
)
// 水滴下落动画(除湿)
val dropOffset by infiniteTransition.animateFloat(
initialValue = -2f,
targetValue = 2f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 800, easing = androidx.compose.animation.core.EaseInOut),
repeatMode = RepeatMode.Reverse
),
label = "drop_offset"
)
// 根据模式决定主题色 // 根据模式决定主题色
val accentColor = when (selectedMode) { val accentColor = when (selectedMode) {
0 -> Color(0xFF4FC3F7) // 制冷 - 蓝色 0 -> Color(0xFF4FC3F7) // 制冷 - 蓝色
@ -1071,6 +1163,9 @@ fun AirConditionerCard(modifier: Modifier = Modifier, roomName: String = "房间
else -> Color(0xFFB0BEC5) // 送风 - 灰色 else -> Color(0xFFB0BEC5) // 送风 - 灰色
} }
// 当前模式的emoji
val currentEmoji = modeIcons[selectedMode].first
Box( Box(
modifier = modifier modifier = modifier
.height(260.dp) .height(260.dp)
@ -1096,7 +1191,7 @@ fun AirConditionerCard(modifier: Modifier = Modifier, roomName: String = "房间
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) { Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
// 空调图标 // 空调图标 - 根据模式应用不同动画
Box( Box(
modifier = Modifier modifier = Modifier
.size(36.dp) .size(36.dp)
@ -1104,7 +1199,17 @@ fun AirConditionerCard(modifier: Modifier = Modifier, roomName: String = "房间
.background(accentColor.copy(alpha = if (isOn) 0.3f else 0.1f)), .background(accentColor.copy(alpha = if (isOn) 0.3f else 0.1f)),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Text(text = "❄️", fontSize = 18.sp) // 根据模式选择动画:除湿用上下移动,其他用旋转
val emojiModifier = when {
!isOn -> Modifier
selectedMode == 2 -> Modifier.offset(y = dropOffset.dp) // 除湿:上下移动
else -> Modifier.rotate(iconRotation) // 其他:旋转
}
Text(
text = currentEmoji,
fontSize = 18.sp,
modifier = emojiModifier
)
} }
Column { Column {
Text(text = "空调", fontWeight = FontWeight.SemiBold, color = Color.White, fontSize = 16.sp) Text(text = "空调", fontWeight = FontWeight.SemiBold, color = Color.White, fontSize = 16.sp)
@ -1177,22 +1282,28 @@ fun AirConditionerCard(modifier: Modifier = Modifier, roomName: String = "房间
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
modes.forEachIndexed { index, (icon, name) -> modeIcons.forEachIndexed { index, (emoji, name) ->
val isSelected = selectedMode == index val isSelected = selectedMode == index
val modeColor = when (index) {
0 -> Color(0xFF4FC3F7)
1 -> Color(0xFFFF8A65)
2 -> Color(0xFF81C784)
else -> Color(0xFFB0BEC5)
}
Box( Box(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.height(44.dp) .height(44.dp)
.clip(RoundedCornerShape(12.dp)) .clip(RoundedCornerShape(12.dp))
.background( .background(
if (isSelected && isOn) accentColor.copy(alpha = 0.4f) if (isSelected && isOn) modeColor.copy(alpha = 0.4f)
else Color(0xFF2A2A3E).copy(alpha = 0.6f) else Color(0xFF2A2A3E).copy(alpha = 0.6f)
) )
.clickable(enabled = isOn) { selectedMode = index }, .clickable(enabled = isOn) { selectedMode = index },
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Column(horizontalAlignment = Alignment.CenterHorizontally) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = icon, fontSize = 14.sp) Text(text = emoji, fontSize = 14.sp)
Text( Text(
text = name, text = name,
fontSize = 10.sp, fontSize = 10.sp,

View File

@ -1,5 +1,6 @@
package com.example.smarthome.ui package com.example.smarthome.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@ -15,9 +16,13 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.smarthome.data.BackgroundManager
@Composable @Composable
fun SettingsScreen(onBack: () -> Unit) { fun SettingsScreen(onBack: () -> Unit) {
@ -75,12 +80,25 @@ fun SettingsContentList() {
}} }}
item { SettingsSection(title = "显示设置") { item { SettingsSection(title = "显示设置") {
var darkMode by remember { mutableStateOf(true) } BackgroundSelector()
val context = LocalContext.current
val selectedBg by BackgroundManager.selectedBackground.collectAsState()
// 深色模式根据当前背景是否为暗色来判断
val isDarkMode = BackgroundManager.backgrounds.getOrNull(selectedBg)?.isDark ?: false
SettingsSwitchItem( SettingsSwitchItem(
title = "深色模式", title = "深色模式",
subtitle = "使用深色主题", subtitle = "使用深色主题",
checked = darkMode, checked = isDarkMode,
onCheckedChange = { darkMode = it } onCheckedChange = { dark ->
// 切换深色/浅色模式时自动更换背景
if (dark) {
BackgroundManager.setBackground(context, 2) // 紫金绸
} else {
BackgroundManager.setBackground(context, 0) // 丝绸白
}
}
) )
SettingsItem( SettingsItem(
@ -355,3 +373,117 @@ fun SettingsContent() {
SettingsContentList() SettingsContentList()
} }
} }
@Composable
fun BackgroundSelector() {
val context = LocalContext.current
val selectedBackground by BackgroundManager.selectedBackground.collectAsState()
val interactionSources = remember {
BackgroundManager.backgrounds.map { MutableInteractionSource() }
}
Column(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(16.dp))
.background(Color(0x33121212))
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
text = "背景壁纸",
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = Color.White
)
Text(
text = "选择您喜欢的背景图片",
fontSize = 13.sp,
color = Color(0xFF9AA0A6)
)
Spacer(modifier = Modifier.height(4.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
BackgroundManager.backgrounds.forEachIndexed { index, bg ->
val isSelected = selectedBackground == bg.id
Column(
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.clip(RoundedCornerShape(10.dp))
.then(
if (isSelected) {
Modifier.border(
width = 2.dp,
brush = Brush.linearGradient(
listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF))
),
shape = RoundedCornerShape(10.dp)
)
} else Modifier
)
.clickable(
indication = null,
interactionSource = interactionSources[index]
) {
BackgroundManager.setBackground(context, bg.id)
}
) {
Image(
painter = painterResource(id = bg.thumbId),
contentDescription = bg.name,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
if (isSelected) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.3f)),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.size(20.dp)
.clip(RoundedCornerShape(10.dp))
.background(
Brush.linearGradient(
listOf(Color(0xFFA9F0FF), Color(0xFFB89CFF))
)
),
contentAlignment = Alignment.Center
) {
Text(
text = "",
color = Color.White,
fontSize = 12.sp,
fontWeight = FontWeight.Bold
)
}
}
}
}
Text(
text = bg.name,
fontSize = 11.sp,
fontWeight = if (isSelected) FontWeight.Medium else FontWeight.Normal,
color = if (isSelected) Color(0xFFA9F0FF) else Color(0xFF9AA0A6)
)
}
}
}
}
}

View File

@ -4,28 +4,8 @@
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<!-- 自动化图标 - 齿轮和箭头 --> <!-- 自动化图标 - 闪电 -->
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M12,2 L10.5,4.5 L7.5,4.5 L6,7 L3.5,8.5 L3.5,11.5 L6,13 L7.5,15.5 L10.5,15.5 L12,18 L13.5,15.5 L16.5,15.5 L18,13 L20.5,11.5 L20.5,8.5 L18,7 L16.5,4.5 L13.5,4.5 Z M12,8 C14.21,8 16,9.79 16,12 C16,14.21 14.21,16 12,16 C9.79,16 8,14.21 8,12 C8,9.79 9.79,8 12,8 Z"/> android:pathData="M13,2 L4,14 L11,14 L11,22 L20,10 L13,10 L13,2 Z"/>
<!-- 内圈 -->
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12,10 C10.9,10 10,10.9 10,12 C10,13.1 10.9,14 12,14 C13.1,14 14,13.1 14,12 C14,10.9 13.1,10 12,10 Z"/>
<!-- 循环箭头 -->
<path
android:strokeColor="#FFFFFFFF"
android:strokeWidth="1.5"
android:strokeLineCap="round"
android:fillColor="#00000000"
android:pathData="M19,12 C19,15.87 15.87,19 12,19 C8.13,19 5,15.87 5,12 M5,12 L7,10 M5,12 L7,14"/>
<path
android:strokeColor="#FFFFFFFF"
android:strokeWidth="1.5"
android:strokeLineCap="round"
android:fillColor="#00000000"
android:pathData="M5,12 C5,8.13 8.13,5 12,5 C15.87,5 19,8.13 19,12 M19,12 L17,10 M19,12 L17,14"/>
</vector> </vector>

View File

@ -4,28 +4,24 @@
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<!-- 场景图标 - 魔法棒 --> <!-- 场景图标 - 四个方块组成的网格 -->
<!-- 左上 -->
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M20,8 L18,8 L18,6 C18,5.45 17.55,5 17,5 C16.45,5 16,5.45 16,6 L16,8 L14,8 C13.45,8 13,8.45 13,9 C13,9.55 13.45,10 14,10 L16,10 L16,12 C16,12.55 16.45,13 17,13 C17.55,13 18,12.55 18,12 L18,10 L20,10 C20.55,10 21,9.55 21,9 C21,8.45 20.55,8 20,8 Z"/> android:pathData="M4,4 L10,4 C10.55,4 11,4.45 11,5 L11,10 C11,10.55 10.55,11 10,11 L4,11 C3.45,11 3,10.55 3,10 L3,5 C3,4.45 3.45,4 4,4 Z"/>
<!-- 右上 -->
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M11,4 L10,4 L10,3 C10,2.45 9.55,2 9,2 C8.45,2 8,2.45 8,3 L8,4 L7,4 C6.45,4 6,4.45 6,5 C6,5.55 6.45,6 7,6 L8,6 L8,7 C8,7.55 8.45,8 9,8 C9.55,8 10,7.55 10,7 L10,6 L11,6 C11.55,6 12,5.55 12,5 C12,4.45 11.55,4 11,4 Z"/> android:pathData="M14,4 L20,4 C20.55,4 21,4.45 21,5 L21,10 C21,10.55 20.55,11 20,11 L14,11 C13.45,11 13,10.55 13,10 L13,5 C13,4.45 13.45,4 14,4 Z"/>
<!-- 左下 -->
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M3.5,11 L3.5,10 L2.5,10 C2.22,10 2,9.78 2,9.5 C2,9.22 2.22,9 2.5,9 L3.5,9 L3.5,8 C3.5,7.72 3.72,7.5 4,7.5 C4.28,7.5 4.5,7.72 4.5,8 L4.5,9 L5.5,9 C5.78,9 6,9.22 6,9.5 C6,9.78 5.78,10 5.5,10 L4.5,10 L4.5,11 C4.5,11.28 4.28,11.5 4,11.5 C3.72,11.5 3.5,11.28 3.5,11 Z"/> android:pathData="M4,13 L10,13 C10.55,13 11,13.45 11,14 L11,19 C11,19.55 10.55,20 10,20 L4,20 C3.45,20 3,19.55 3,19 L3,14 C3,13.45 3.45,13 4,13 Z"/>
<!-- 魔法棒主体 --> <!-- 右下 -->
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M15.5,14.5 L4.5,3.5 C4.11,3.11 3.48,3.11 3.09,3.5 L3.5,3.91 L14.5,14.91 L15.5,14.5 Z"/> android:pathData="M14,13 L20,13 C20.55,13 21,13.45 21,14 L21,19 C21,19.55 20.55,20 20,20 L14,20 C13.45,20 13,19.55 13,19 L13,14 C13,13.45 13.45,13 14,13 Z"/>
<path
android:strokeColor="#FFFFFFFF"
android:strokeWidth="2"
android:strokeLineCap="round"
android:fillColor="#00000000"
android:pathData="M15,15 L4,4"/>
</vector> </vector>

View File

@ -1,3 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <vector xmlns:android="http://schemas.android.com/apk/res/android"
<path android:strokeColor="#FFFFFFFF" android:strokeWidth="2" android:fillColor="#00000000" android:pathData="M12,3 L21,8 L12,21 L3,8 Z"/> android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<!-- 安全图标 - 盾牌 -->
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12,1 L3,5 L3,11 C3,16.55 6.84,21.74 12,23 C17.16,21.74 21,16.55 21,11 L21,5 L12,1 Z M12,11.99 L19,11.99 C18.47,16.11 15.72,19.78 12,20.93 L12,12 L5,12 L5,6.3 L12,3.19 L12,11.99 Z"/>
</vector> </vector>

View File

@ -4,29 +4,19 @@
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<!-- 统计图标 - 柱状图 --> <!-- 统计图标 - 饼图 -->
<!-- 圆环 -->
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M5,19 L5,9 C5,8.45 5.45,8 6,8 L8,8 C8.55,8 9,8.45 9,9 L9,19 C9,19.55 8.55,20 8,20 L6,20 C5.45,20 5,19.55 5,19 Z"/> android:pathData="M12,2 C6.48,2 2,6.48 2,12 C2,17.52 6.48,22 12,22 C17.52,22 22,17.52 22,12 C22,6.48 17.52,2 12,2 Z M12,20 C7.59,20 4,16.41 4,12 C4,7.59 7.59,4 12,4 C16.41,4 20,7.59 20,12 C20,16.41 16.41,20 12,20 Z"/>
<!-- 扇形部分 -->
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M10,19 L10,5 C10,4.45 10.45,4 11,4 L13,4 C13.55,4 14,4.45 14,5 L14,19 C14,19.55 13.55,20 13,20 L11,20 C10.45,20 10,19.55 10,19 Z"/> android:pathData="M12,12 L12,4 C16.41,4 20,7.59 20,12 L12,12 Z"/>
<!-- 小扇形 -->
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M15,19 L15,12 C15,11.45 15.45,11 16,11 L18,11 C18.55,11 19,11.45 19,12 L19,19 C19,19.55 18.55,20 18,20 L16,20 C15.45,20 15,19.55 15,19 Z"/> android:pathData="M12,12 L20,12 C20,14.5 19,16.8 17.3,18.3 L12,12 Z"/>
<!-- 趋势线 -->
<path
android:strokeColor="#FFFFFFFF"
android:strokeWidth="1.5"
android:strokeLineCap="round"
android:fillColor="#00000000"
android:pathData="M3,17 L7,13 L12,15 L17,9 L21,11"/>
<!-- 箭头 -->
<path
android:fillColor="#FFFFFFFF"
android:pathData="M21,11 L19,9 L19,13 Z"/>
</vector> </vector>