feat: 状态栏优化 — 背景吸附+连接按钮合并+隐藏系统状态栏
- StatusBar 重构为 Box + Row 双层结构,背景延伸到屏幕顶端 - 连接按钮合并到顶部 ConnectionIndicator,删除底部独立连接按钮 - ConnectionIndicator 改为按钮形态(Surface + 圆角边框) - 隐藏系统状态栏(WindowInsetsControllerCompat),压缩 StatusBar 高度 Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
@@ -133,12 +133,14 @@ val y = power / 100.0 * sin(angle) // 水平分量
|
|||||||
|
|
||||||
## 构建与运行
|
## 构建与运行
|
||||||
|
|
||||||
```bash
|
> **注意**:项目源码位于 Windows 目录下,通过 WSL 访问和编辑。WSL 环境中没有 Android SDK / Gradle,无法直接运行构建命令。如需构建或安装,请在 Windows 端的 Android Studio 或终端中执行。
|
||||||
# 在项目根目录
|
|
||||||
./gradlew assembleDebug
|
|
||||||
|
|
||||||
# 安装到设备
|
```bash
|
||||||
./gradlew installDebug
|
# 在项目根目录(Windows 端)
|
||||||
|
gradlew assembleDebug
|
||||||
|
|
||||||
|
# 安装到设备(Windows 端)
|
||||||
|
gradlew installDebug
|
||||||
```
|
```
|
||||||
|
|
||||||
### 依赖
|
### 依赖
|
||||||
@@ -194,9 +196,12 @@ val y = power / 100.0 * sin(angle) // 水平分量
|
|||||||
- [x] UI 布局:增加 bg1.png 作为底图,无视频流时显示(视频流播放时覆盖底图)
|
- [x] UI 布局:增加 bg1.png 作为底图,无视频流时显示(视频流播放时覆盖底图)
|
||||||
- [x] UI 布局:开关灯合并为前灯/后灯两个按键,通过颜色(琥珀色=开/暗色=关)表示置位状态,配合机器人回报状态同步
|
- [x] UI 布局:开关灯合并为前灯/后灯两个按键,通过颜色(琥珀色=开/暗色=关)表示置位状态,配合机器人回报状态同步
|
||||||
- [x] 状态栏:ICMP 延迟测量 + WiFi 信号强度图标显示,放置于连接状态文字右侧,延迟独立于连接生命周期
|
- [x] 状态栏:ICMP 延迟测量 + WiFi 信号强度图标显示,放置于连接状态文字右侧,延迟独立于连接生命周期
|
||||||
|
- [x] UI 布局:StatusBar 背景延伸到屏幕顶端,视觉吸附优化(Box + Row 双层结构,背景填满状态栏区域)
|
||||||
|
- [x] UI 布局:连接按钮合并到顶部 StatusBar 的 ConnectionIndicator,删除底部独立连接按钮
|
||||||
|
- [x] UI 布局:ConnectionIndicator 改为按钮形态(Surface + 圆角边框),仅指示器区域可点击切换连接
|
||||||
|
- [x] UI 布局:隐藏系统状态栏(WindowInsetsControllerCompat),压缩自定义 StatusBar 高度,扩展可视区域
|
||||||
|
|
||||||
### 待办
|
### 待办
|
||||||
|
|
||||||
- [ ] UI 布局:header 部分虽已避开状态栏,但底色与屏幕顶端视觉不吸附,需优化视觉传达效果
|
|
||||||
- [ ] UI 布局:功能按钮均布在四周,充分利用屏幕空间,优化单手操作体验
|
- [ ] UI 布局:功能按钮均布在四周,充分利用屏幕空间,优化单手操作体验
|
||||||
- [ ] 视频进一步延迟优化:探索更激进的低延迟策略(如降低帧率、缩小分辨率、调整 ExoPlayer 缓冲策略等)
|
- [ ] 视频进一步延迟优化:探索更激进的低延迟策略(如降低帧率、缩小分辨率、调整 ExoPlayer 缓冲策略等)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import androidx.activity.compose.setContent
|
|||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
@@ -18,6 +20,11 @@ class MainActivity : ComponentActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
|
// 隐藏系统状态栏,顶部区域完全由自定义 StatusBar 接管
|
||||||
|
WindowInsetsControllerCompat(window, window.decorView).apply {
|
||||||
|
hide(WindowInsetsCompat.Type.statusBars())
|
||||||
|
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||||
|
}
|
||||||
setContent {
|
setContent {
|
||||||
M20_gamepadTheme {
|
M20_gamepadTheme {
|
||||||
AppNavGraph()
|
AppNavGraph()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.example.m20_gamepad.ui
|
|||||||
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -14,7 +15,6 @@ import androidx.compose.foundation.layout.height
|
|||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.statusBars
|
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
@@ -134,9 +134,14 @@ fun MainScreen(
|
|||||||
wifiSignalLevel = wifiSignalLevel,
|
wifiSignalLevel = wifiSignalLevel,
|
||||||
onStatusClick = { statusPanelVisible = true },
|
onStatusClick = { statusPanelVisible = true },
|
||||||
onSettingsClick = onSettingsClick,
|
onSettingsClick = onSettingsClick,
|
||||||
modifier = Modifier
|
onConnectToggle = {
|
||||||
.align(Alignment.TopCenter)
|
if (connectionState == ProtocolClient.State.CONNECTED) {
|
||||||
.windowInsetsPadding(WindowInsets.statusBars)
|
viewModel.disconnect()
|
||||||
|
} else {
|
||||||
|
viewModel.connect()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.align(Alignment.TopCenter)
|
||||||
)
|
)
|
||||||
|
|
||||||
// ---- 底部控制区 ----
|
// ---- 底部控制区 ----
|
||||||
@@ -180,19 +185,24 @@ private fun StatusBar(
|
|||||||
wifiSignalLevel: Int,
|
wifiSignalLevel: Int,
|
||||||
onStatusClick: () -> Unit,
|
onStatusClick: () -> Unit,
|
||||||
onSettingsClick: () -> Unit,
|
onSettingsClick: () -> Unit,
|
||||||
|
onConnectToggle: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
Row(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.background(OverlayBg)
|
.background(OverlayBg)
|
||||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
|
||||||
) {
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 12.dp, vertical = 4.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
// 连接状态 + WiFi 信号 + Ping 延迟
|
// 连接状态 + WiFi 信号 + Ping 延迟
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
ConnectionIndicator(connectionState)
|
ConnectionIndicator(connectionState, onConnectToggle)
|
||||||
Spacer(Modifier.width(12.dp))
|
Spacer(Modifier.width(12.dp))
|
||||||
WifiSignalIcon(level = wifiSignalLevel)
|
WifiSignalIcon(level = wifiSignalLevel)
|
||||||
Spacer(Modifier.width(12.dp))
|
Spacer(Modifier.width(12.dp))
|
||||||
@@ -231,10 +241,11 @@ private fun StatusBar(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ConnectionIndicator(state: ProtocolClient.State) {
|
private fun ConnectionIndicator(state: ProtocolClient.State, onClick: () -> Unit) {
|
||||||
val (color, text) = when (state) {
|
val (color, text) = when (state) {
|
||||||
ProtocolClient.State.CONNECTED -> Color(0xFF4CAF50) to "已连接"
|
ProtocolClient.State.CONNECTED -> Color(0xFF4CAF50) to "已连接"
|
||||||
ProtocolClient.State.CONNECTING -> Color(0xFFFFC107) to "连接中"
|
ProtocolClient.State.CONNECTING -> Color(0xFFFFC107) to "连接中"
|
||||||
@@ -242,14 +253,24 @@ private fun ConnectionIndicator(state: ProtocolClient.State) {
|
|||||||
ProtocolClient.State.ERROR -> Color(0xFFF44336) to "错误"
|
ProtocolClient.State.ERROR -> Color(0xFFF44336) to "错误"
|
||||||
ProtocolClient.State.DISCONNECTED -> Color(0xFF9E9E9E) to "未连接"
|
ProtocolClient.State.DISCONNECTED -> Color(0xFF9E9E9E) to "未连接"
|
||||||
}
|
}
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Surface(
|
||||||
Box(
|
onClick = onClick,
|
||||||
modifier = Modifier
|
shape = RoundedCornerShape(14.dp),
|
||||||
.size(10.dp)
|
border = BorderStroke(1.dp, Color(0x55FFFFFF)),
|
||||||
.background(color, CircleShape)
|
color = Color(0x22000000)
|
||||||
)
|
) {
|
||||||
Spacer(Modifier.width(6.dp))
|
Row(
|
||||||
Text(text, color = OverlayText, fontSize = 14.sp)
|
modifier = Modifier.padding(horizontal = 10.dp, vertical = 2.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(10.dp)
|
||||||
|
.background(color, CircleShape)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(6.dp))
|
||||||
|
Text(text, color = OverlayText, fontSize = 14.sp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,19 +397,6 @@ private fun ControlButtons(
|
|||||||
.verticalScroll(scrollState)
|
.verticalScroll(scrollState)
|
||||||
.padding(top = 4.dp, bottom = 4.dp)
|
.padding(top = 4.dp, bottom = 4.dp)
|
||||||
) {
|
) {
|
||||||
// 连接/断开
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
if (connected) viewModel.disconnect() else viewModel.connect()
|
|
||||||
},
|
|
||||||
colors = ButtonDefaults.buttonColors(
|
|
||||||
containerColor = if (connected) Color(0xFFF44336) else Color(0xFF4CAF50)
|
|
||||||
),
|
|
||||||
modifier = Modifier.width(110.dp)
|
|
||||||
) {
|
|
||||||
Text(if (connected) "断开" else "连接")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 运动状态转换
|
// 运动状态转换
|
||||||
ControlButton(
|
ControlButton(
|
||||||
"起立",
|
"起立",
|
||||||
|
|||||||
Reference in New Issue
Block a user