fix: 修正终审发现的详情页配色、空态与血压卡可点击等问题
- 详情页柱状图改用指标语义色(新增 MetricType→色板映射),此前恒定绿色 - 详情页无数据时渲染空态文案,不再画全 0 柱状图与三行 0.0 - 首页「未开启」指标卡不再可点击,与详情页内容保持一致 - 压力卡单位位显示等级文案(如「34 正常」),接上原先无人调用的 stressLevelLabelRes - NavHost 补 consumeWindowInsets,避免详情页 TopAppBar 重复叠加状态栏内边距 - README 注明 installDebug 需要设备/模拟器 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -24,8 +24,11 @@
|
||||
|
||||
```bash
|
||||
./gradlew assembleDebug # 产物在 app/build/outputs/apk/debug/
|
||||
./gradlew installDebug # 安装到已连接设备
|
||||
./gradlew installDebug # 安装到已连接设备(需接好设备或已启动模拟器)
|
||||
./gradlew test # 单元测试
|
||||
```
|
||||
|
||||
> `installDebug` 需要一台已连接的设备或正在运行的模拟器。M1 使用的机器两者都没有,
|
||||
> 因此本机实际可执行的是 `./gradlew assembleDebug` 与 `./gradlew :app:testDebugUnitTest`。
|
||||
|
||||
要求:JDK 17、Android SDK(platform 36 / build-tools 36.1.0)。
|
||||
|
||||
+3
-2
@@ -30,7 +30,8 @@ import com.placeholder.ringhealth.core.designsystem.TextSecondary
|
||||
* @param value 主数值文本。
|
||||
* @param unit 单位文本,如「步」「次/分」。
|
||||
* @param modifier 外部修饰符。
|
||||
* @param onClick 点击回调,默认空实现(配合 HealthCard 的整卡可点击)。
|
||||
* @param onClick 点击回调;为 null 表示卡片不可点击 —— 不挂 clickable,因此既没有水波纹
|
||||
* 也不会触发导航,用于「未开启」这类没有数值的指标卡(有值的卡才可跳转详情)。
|
||||
*/
|
||||
@Composable
|
||||
fun MetricTile(
|
||||
@@ -40,7 +41,7 @@ fun MetricTile(
|
||||
value: String,
|
||||
unit: String,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: () -> Unit = {},
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
HealthCard(modifier = modifier, onClick = onClick) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.placeholder.ringhealth.core.navigation
|
||||
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.FitnessCenter
|
||||
@@ -90,7 +91,12 @@ fun RingHealthApp(navController: NavHostController = rememberNavController()) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = Routes.HOME,
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
// consumeWindowInsets 必须跟着 padding 一起写:外层 Scaffold 没有 topBar,
|
||||
// 它的 innerPadding.top 就是状态栏高度,若只 padding 不消费,这个 inset 会继续
|
||||
// 往下传,详情页内层 Scaffold 的 TopAppBar 会再加一次状态栏内边距 ——
|
||||
// AppBar 被推低约两个状态栏,上方露出一条背景色带,与四个 Tab 页明显不同。
|
||||
// 消费后内层拿到的是已扣减的 inset,两个方向都安全(若外层早已消费,这里是 no-op)。
|
||||
modifier = Modifier.padding(innerPadding).consumeWindowInsets(innerPadding),
|
||||
) {
|
||||
composable(Routes.HOME) {
|
||||
HomeScreen(onOpenDetail = { metric -> navController.navigate(Routes.detail(metric)) })
|
||||
|
||||
@@ -37,7 +37,12 @@ import com.placeholder.ringhealth.R
|
||||
import com.placeholder.ringhealth.core.common.formatClock
|
||||
import com.placeholder.ringhealth.core.common.formatDecimal
|
||||
import com.placeholder.ringhealth.core.designsystem.MetricBlue
|
||||
import com.placeholder.ringhealth.core.designsystem.MetricCyan
|
||||
import com.placeholder.ringhealth.core.designsystem.MetricGreen
|
||||
import com.placeholder.ringhealth.core.designsystem.MetricOrange
|
||||
import com.placeholder.ringhealth.core.designsystem.MetricPurple
|
||||
import com.placeholder.ringhealth.core.designsystem.MetricRed
|
||||
import com.placeholder.ringhealth.core.designsystem.MetricYellow
|
||||
import com.placeholder.ringhealth.core.designsystem.RingHealthTheme
|
||||
import com.placeholder.ringhealth.core.designsystem.TextSecondary
|
||||
import com.placeholder.ringhealth.core.designsystem.component.HealthCard
|
||||
@@ -75,6 +80,8 @@ fun MetricDetailScreen(
|
||||
/**
|
||||
* 无状态指标详情内容:范围切换 + 柱状趋势图 + 统计摘要(+ 睡眠分期)。
|
||||
*
|
||||
* 范围切换常驻;当前范围没有数据点时,只把「趋势图 + 统计摘要」换成一行空态文案。
|
||||
*
|
||||
* 标题取自 `state.metric` 而不是外面传进来的 `metric` —— 无状态层只有 `state` 一个数据来源,
|
||||
* 免得出现「标题写心率、数据画睡眠」这种两者不一致的可能。
|
||||
*/
|
||||
@@ -118,13 +125,35 @@ private fun MetricDetailContent(
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
// 范围切换常驻,空数据时反而更要留着:切到另一个范围是用户看到「暂无数据」后的
|
||||
// 第一反应,chips 也是判断「哪个范围是空的」的唯一线索。它属于页头,不属于数据区。
|
||||
item { RangeSelector(current = state.range, onSelect = onSelectRange) }
|
||||
|
||||
// 只有图与统计被替换为空态。判空必须在 loading 之后:Mock 有约 200ms 延迟,
|
||||
// 加载期间 points 也是空的,先判空会在数据到达前闪一下空态(loading 分支已提前 return)。
|
||||
if (state.points.isEmpty()) {
|
||||
// 不再渲染「全 0 高度的柱状图 + 三行 0.0」,那看着像页面坏了而不是「没有数据」。
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 48.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.detail_empty),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = TextSecondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
return@LazyColumn
|
||||
}
|
||||
|
||||
item {
|
||||
HealthCard {
|
||||
SectionHeader(title = stringResource(R.string.detail_trend))
|
||||
MetricBarChart(
|
||||
values = state.points.map { it.value.toFloat() },
|
||||
color = MetricGreen,
|
||||
color = metricTint(state.metric),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -228,6 +257,23 @@ private fun metricTitleRes(type: MetricType): Int = when (type) {
|
||||
MetricType.BLOOD_PRESSURE -> R.string.metric_blood_pressure
|
||||
}
|
||||
|
||||
/**
|
||||
* 指标类型 → 语义色。
|
||||
*
|
||||
* 与 `HomeScreen` 里各指标卡的 tint 一一对应(步数=绿、心率=红、睡眠=蓝、血氧=青、
|
||||
* 压力=紫、体温=橙、血压=黄)。色板的唯一来源是设计文档 §指标色板与
|
||||
* `core/designsystem/Color.kt`,本函数只做映射,不在页面里另造颜色。
|
||||
*/
|
||||
private fun metricTint(type: MetricType): Color = when (type) {
|
||||
MetricType.STEP -> MetricGreen
|
||||
MetricType.HEART_RATE -> MetricRed
|
||||
MetricType.SLEEP -> MetricBlue
|
||||
MetricType.BLOOD_OXYGEN -> MetricCyan
|
||||
MetricType.STRESS -> MetricPurple
|
||||
MetricType.TEMPERATURE -> MetricOrange
|
||||
MetricType.BLOOD_PRESSURE -> MetricYellow
|
||||
}
|
||||
|
||||
/** 周/月切换。 */
|
||||
@Composable
|
||||
private fun RangeSelector(
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.placeholder.ringhealth.core.common.formatCalories
|
||||
import com.placeholder.ringhealth.core.common.formatDistance
|
||||
import com.placeholder.ringhealth.core.common.formatSleepDuration
|
||||
import com.placeholder.ringhealth.core.common.formatSteps
|
||||
import com.placeholder.ringhealth.core.common.stressLevelLabelRes
|
||||
import com.placeholder.ringhealth.core.designsystem.MetricBlue
|
||||
import com.placeholder.ringhealth.core.designsystem.MetricCyan
|
||||
import com.placeholder.ringhealth.core.designsystem.MetricGreen
|
||||
@@ -109,13 +110,16 @@ private fun HomeContent(
|
||||
item { StepsCard(summary = summary, onClick = { onOpenDetail(MetricType.STEP) }) }
|
||||
item { SectionHeader(title = stringResource(R.string.home_metrics)) }
|
||||
items(cards) { card ->
|
||||
// 没有数值(未开启)的指标卡不可点击:传 null 后 MetricTile/HealthCard 不挂 clickable,
|
||||
// 无跳转也无水波纹。否则首页显示「未开启」、点进去却是一张完整图表,两页自相矛盾。
|
||||
val openDetail = card.value?.let { { onOpenDetail(card.type) } }
|
||||
MetricTile(
|
||||
icon = card.icon,
|
||||
iconTint = card.tint,
|
||||
title = card.title,
|
||||
value = card.value ?: notEnabledText,
|
||||
unit = if (card.value == null) "" else card.unit,
|
||||
onClick = { onOpenDetail(card.type) },
|
||||
onClick = openDetail,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -239,7 +243,10 @@ private fun buildMetricCards(summary: HealthSummary): List<MetricCardSpec> = lis
|
||||
tint = MetricPurple,
|
||||
title = stringResource(R.string.metric_stress),
|
||||
value = summary.stress?.toString(),
|
||||
unit = "",
|
||||
// 单位位改放压力等级文案(数值落在哪个区间就是放松/正常/中等/偏高),
|
||||
// 于是卡片读作「34 正常」而不是一个孤零零的数字。无值时留空(上面的 value 分支
|
||||
// 会整卡退化成「未开启」,此时单位本来也会被清空)。
|
||||
unit = summary.stress?.let { stringResource(stressLevelLabelRes(it)) } ?: "",
|
||||
),
|
||||
MetricCardSpec(
|
||||
type = MetricType.TEMPERATURE,
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
<string name="detail_max">最高</string>
|
||||
<string name="detail_min">最低</string>
|
||||
<string name="detail_trend">趋势</string>
|
||||
<string name="detail_empty">暂无数据</string>
|
||||
<string name="detail_sleep_stages">睡眠分期</string>
|
||||
<string name="detail_sleep_deep">深睡</string>
|
||||
<string name="detail_sleep_light">浅睡</string>
|
||||
|
||||
Reference in New Issue
Block a user