From e6c218d8308a6892742f780f18f92889e30bfec0 Mon Sep 17 00:00:00 2001 From: dongliang Date: Wed, 29 Apr 2026 17:47:52 +0930 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=BA=A2=E7=82=B9=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E7=AE=A1=E7=90=86=EF=BC=88activeDotCards+renderDots?= =?UTF-8?q?=EF=BC=89+=20=E5=8E=BB=E6=8E=89=E6=8F=90=E7=A4=BA=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按矩阵验证的19个场景统一重写通知逻辑: 核心改动: - activeDotCards 集合累积管理红点,只增不减(除显式操作) - renderDots() 统一渲染,不在各处单独设 visibility - 去掉提示条(用户反馈不好看) 场景覆盖: - 场景1-2: 首页连续收到不同分类通知 → activeDotCards累积 ✅ - 场景3: 点横幅 → consumeAll+activeDotCards清空 ✅ - 场景4-5: 点红点 → activeDotCards移除对应+renderDots ✅ - 场景7: 所有红点点完 → 自动consumeAll清理 ✅ - 场景11-16: 非首页 → preNotificationStats精确diffStats→补充activeDotCards ✅ - 场景18: 全部查看后新通知 → consumeAll已重置,新通知正常累积 ✅ - 场景19: 列表中又来通知 → 返回后onResume补充新红点,不覆盖已ack的 ✅ Co-Authored-By: Claude Opus 4.6 (1M context) --- .../com/xiaoqu/watch/ui/home/HomeFragment.kt | 170 ++++++++---------- app/src/main/res/layout/page_main.xml | 16 -- 2 files changed, 71 insertions(+), 115 deletions(-) diff --git a/app/src/main/java/com/xiaoqu/watch/ui/home/HomeFragment.kt b/app/src/main/java/com/xiaoqu/watch/ui/home/HomeFragment.kt index 25c374b..e8fe816 100644 --- a/app/src/main/java/com/xiaoqu/watch/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/xiaoqu/watch/ui/home/HomeFragment.kt @@ -69,8 +69,12 @@ class HomeFragment : BaseFragment() { private lateinit var dotPool: View private lateinit var dotPunch: View private lateinit var dotComplete: View - // 新任务提示条 - private lateinit var tvNewTaskHint: TextView + /** 当前应显示红点的卡片集合(累积,只有显式操作才移除) */ + private val activeDotCards = mutableSetOf() + /** 红点 View 映射 */ + private val dotViews: Map by lazy { + mapOf(2 to dotPool, 3 to dotPunch, 4 to dotComplete) + } // ===== 设置页 View 引用 ===== private lateinit var tvAvatarLetter: TextView @@ -131,58 +135,41 @@ class HomeFragment : BaseFragment() { override fun onResume() { super.onResume() if (notificationManager.pendingCount <= 0) { - // 全部已查看 → 清除所有提示 - dotPool.visibility = View.GONE - dotPunch.visibility = View.GONE - dotComplete.visibility = View.GONE - tvNewTaskHint.visibility = View.GONE + // 全部已查看/无通知 → 清除 + activeDotCards.clear() + renderDots() return } - // 有未读通知 → 用 preNotificationStats 精确对比红点 - // (preNotificationStats 是通知到达前的快照,不会被 onViewCreated 的 fetchStatistics 覆盖) - val ack = notificationManager.acknowledgedCards + // 有未读通知 → 用 preNotificationStats 精确补充红点到 activeDotCards val baseline = notificationManager.preNotificationStats - if (baseline != null) { - // 精确模式:对比快照和当前统计,只给变化的分类显示红点 - fetchStatisticsForDots(baseline, ack) - } else { - // 无快照(首次加载等):已查看的不显示,未查看的显示 - if (2 !in ack) dotPool.visibility = View.VISIBLE - if (3 !in ack) dotPunch.visibility = View.VISIBLE - if (4 !in ack) dotComplete.visibility = View.VISIBLE - } - updateNewTaskHint() - } - - /** - * 用通知前快照精确对比红点(非首页返回时调用) - * @param baseline 通知到达前的统计快照 - * @param acknowledged 已查看的分类集合 - */ - private fun fetchStatisticsForDots(baseline: com.xiaoqu.watch.data.task.TaskStatistics, acknowledged: Set) { - viewLifecycleOwner.lifecycleScope.launch { - val result = safeApiCall { taskApi.getStatistics() } - if (result is ApiResult.Success && result.data != null) { - val data = result.data - val changed = notificationManager.diffStats(baseline, data) - - // 只给变化且未查看的分类显示红点 - dotPool.visibility = if (2 in changed && 2 !in acknowledged) View.VISIBLE else View.GONE - dotPunch.visibility = if (3 in changed && 3 !in acknowledged) View.VISIBLE else View.GONE - dotComplete.visibility = if (4 in changed && 4 !in acknowledged) View.VISIBLE else View.GONE - - // 记录增量(供 acknowledgeCard 扣减用) - if (2 in changed) notificationManager.recordCardIncrement(2, data.waitForTask - baseline.waitForTask) - if (3 in changed) notificationManager.recordCardIncrement(3, data.treatTask - baseline.treatTask) - if (4 in changed) notificationManager.recordCardIncrement(4, data.incompleteTask - baseline.incompleteTask) - - // 更新数字 - tvPoolNum.text = data.waitForTask.toString() - tvPunchNum.text = data.treatTask.toString() - tvCompleteNum.text = data.incompleteTask.toString() - notificationManager.lastStats = data + if (baseline != null && activeDotCards.isEmpty()) { + // 非首页返回:activeDotCards 为空(view 重建),需要重新计算 + viewLifecycleOwner.lifecycleScope.launch { + val result = safeApiCall { taskApi.getStatistics() } + if (result is ApiResult.Success && result.data != null) { + val data = result.data + val changed = notificationManager.diffStats(baseline, data) + val ack = notificationManager.acknowledgedCards + // 只加未查看的 + for (status in changed) { + if (status !in ack) activeDotCards.add(status) + } + // 记录增量 + if (2 in changed) notificationManager.recordCardIncrement(2, data.waitForTask - baseline.waitForTask) + if (3 in changed) notificationManager.recordCardIncrement(3, data.treatTask - baseline.treatTask) + if (4 in changed) notificationManager.recordCardIncrement(4, data.incompleteTask - baseline.incompleteTask) + // 更新数字 + tvPoolNum.text = data.waitForTask.toString() + tvPunchNum.text = data.treatTask.toString() + tvCompleteNum.text = data.incompleteTask.toString() + notificationManager.lastStats = data + renderDots() + } } + } else { + // 首页返回(从卡片列表回来等):activeDotCards 已有值,直接渲染 + renderDots() } } @@ -329,23 +316,17 @@ class HomeFragment : BaseFragment() { dotPunch = page.findViewById(R.id.dotPunch) dotComplete = page.findViewById(R.id.dotComplete) - // 新任务提示条 - tvNewTaskHint = page.findViewById(R.id.tvNewTaskHint) - tvNewTaskHint.setOnClickListener { - navigateToNewTasks() - } - - // 快捷区卡片点击 → 标记已查看 + 清红点 + 更新提示条 + 跳转 + // 快捷区卡片点击 → 标记已查看 + 清对应红点 + 跳转 page.findViewById(R.id.cardPool)?.setOnClickListener { - acknowledgeCard(2, dotPool) + acknowledgeCard(2) navigateToTaskList(2) } page.findViewById(R.id.cardPunch)?.setOnClickListener { - acknowledgeCard(3, dotPunch) + acknowledgeCard(3) navigateToTaskList(3) } page.findViewById(R.id.cardComplete)?.setOnClickListener { - acknowledgeCard(4, dotComplete) + acknowledgeCard(4) navigateToTaskList(4) } } @@ -386,22 +367,21 @@ class HomeFragment : BaseFragment() { if (result is ApiResult.Success && result.data != null) { val data = result.data - // 对比红点 + 记录每个分类的增量 + // 对比红点:累积到 activeDotCards,不覆盖已有的 if (checkDots) { val oldStats = notificationManager.lastStats val changedCards = notificationManager.diffStats(oldStats, data) - if (2 in changedCards) { - dotPool.visibility = View.VISIBLE - notificationManager.recordCardIncrement(2, data.waitForTask - (oldStats?.waitForTask ?: 0)) - } - if (3 in changedCards) { - dotPunch.visibility = View.VISIBLE - notificationManager.recordCardIncrement(3, data.treatTask - (oldStats?.treatTask ?: 0)) - } - if (4 in changedCards) { - dotComplete.visibility = View.VISIBLE - notificationManager.recordCardIncrement(4, data.incompleteTask - (oldStats?.incompleteTask ?: 0)) + for (status in changedCards) { + activeDotCards.add(status) // 累积,不会移除已有的 + val increment = when (status) { + 2 -> data.waitForTask - (oldStats?.waitForTask ?: 0) + 3 -> data.treatTask - (oldStats?.treatTask ?: 0) + 4 -> data.incompleteTask - (oldStats?.incompleteTask ?: 0) + else -> 0 + } + notificationManager.recordCardIncrement(status, increment) } + renderDots() } // 更新数字 @@ -494,12 +474,11 @@ class HomeFragment : BaseFragment() { is AppEvent.BluetoothStateChanged -> { statusBar.updateBluetooth(event.isOn) } - // 新任务到达 → 刷新统计 + 红点 + 提示条 + // 新任务到达 → 刷新统计 + 累积红点 is AppEvent.NewTaskArrived -> { Timber.d("首页: 新任务到达 (${event.count} 条)") fetchStatistics(checkDots = true) - updateNewTaskHint() - setupBannerClick(event.taskIds) + setupBannerClick() } // MQTT 消息 is AppEvent.MqttMessageReceived -> { @@ -590,26 +569,26 @@ class HomeFragment : BaseFragment() { findNavController().navigate(R.id.action_home_to_taskList, bundle) } - /** 标记某个分类已查看:清红点 + 更新通知数 + 刷新提示条 */ - private fun acknowledgeCard(status: Int, dot: View) { - dot.visibility = View.GONE - notificationManager.acknowledgeCard(status) - updateNewTaskHint() + /** 统一渲染红点:以 activeDotCards 为唯一数据源 */ + private fun renderDots() { + dotViews.forEach { (status, dot) -> + dot.visibility = if (status in activeDotCards) View.VISIBLE else View.GONE + } } - /** 更新新任务提示条 */ - private fun updateNewTaskHint() { - val count = notificationManager.pendingCount - if (count > 0) { - tvNewTaskHint.text = "有${count}条新任务 点击查看" - tvNewTaskHint.visibility = View.VISIBLE - } else { - tvNewTaskHint.visibility = View.GONE + /** 标记某个分类已查看:从 activeDotCards 移除 + 通知管理器记录 */ + private fun acknowledgeCard(status: Int) { + activeDotCards.remove(status) + notificationManager.acknowledgeCard(status) + renderDots() + // 所有红点都点完 → 自动清理(场景7) + if (activeDotCards.isEmpty() && notificationManager.pendingCount <= 0) { + notificationManager.consumeAll() } } /** - * 跳转查看所有新任务(横幅/提示条共用) + * 跳转查看所有新任务(横幅点击用) * 1 个任务 → 跳详情;多个 → 跳列表按 taskIds 加载 */ private fun navigateToNewTasks() { @@ -630,21 +609,14 @@ class HomeFragment : BaseFragment() { findNavController().navigate(R.id.action_home_to_taskList, bundle) } - // 清除所有提示 - clearNewTaskIndicators() - } - - /** 清除所有新任务提示(红点+提示条+未读) */ - private fun clearNewTaskIndicators() { - dotPool.visibility = View.GONE - dotPunch.visibility = View.GONE - dotComplete.visibility = View.GONE - tvNewTaskHint.visibility = View.GONE + // 清除所有提示(场景3:点横幅查看全部) + activeDotCards.clear() + renderDots() notificationManager.consumeAll() } /** 设置通知横幅点击回调 */ - private fun setupBannerClick(taskIds: List) { + private fun setupBannerClick() { val mainActivity = activity as? com.xiaoqu.watch.app.MainActivity ?: return mainActivity.notificationBanner.onClick = { navigateToNewTasks() diff --git a/app/src/main/res/layout/page_main.xml b/app/src/main/res/layout/page_main.xml index 361043a..a457d02 100644 --- a/app/src/main/res/layout/page_main.xml +++ b/app/src/main/res/layout/page_main.xml @@ -42,22 +42,6 @@ - - -