¥
立即购买

安卓应用内反馈提示设计专家

47 浏览
3 试用
0 购买
Nov 30, 2025更新

本提示词专为安卓应用开发场景设计,能够根据具体功能模块和使用情境,生成专业、精准的应用内反馈提示。通过系统化的需求分析和多维度设计考量,确保反馈提示具备清晰的用户引导性、恰当的技术实现性和良好的用户体验。适用于新功能上线、用户行为引导、问题反馈收集等多种应用场景,帮助开发者提升用户参与度和产品改进效率。

场景分析

  • 目标功能与核心路径
    • 新增“离线朗读”:一键下载文章 → 使用系统TTS朗读 → 支持蓝牙控制、倍速、句子高亮、进度同步 → 弱网/锁屏自动切离线。
    • 典型使用场景:通勤/运动(蓝牙耳机)、睡前(锁屏)、弱网环境。
  • 反馈需求与问题定位目标
    • 使用体验类反馈为主,辅助问题定位,重点维度:
      • 朗读停顿/卡顿
      • 发音自然度(语速、语调、标点停顿、英文/数字读法)
      • 耗电异常
      • 通知或其他音频打断
      • 倍速/进度/高亮不同步
      • 蓝牙耳机控制不灵敏
      • 离线切换不稳定
  • 反馈触达关键时机(最小打扰)
    • 首次进入:功能引导与一次性体验偏好采集
    • 播放中:轻量即时情绪反馈(正/负/一般)
    • 发生典型异常后:快速标注原因(如被其他音频打断)
    • 完成一次有效播放后:简短满意度问询
    • 设置页/通知内:随时可达的主动反馈入口
  • 用户与约束
    • 普通用户为主:提示需通俗、低认知负担;避免术语堆叠。
    • 隐私与权限:不收集语音内容;仅在用户明确同意时附带匿名诊断日志;不索取个人信息。
    • 频控:避免频繁打扰;遵守Material 3交互规范;保持与应用品牌一致。

设计方案

一、反馈触达时机与交互流程

  1. 首次进入阅读页(功能引导,底部半屏)
  • 组件:Modal Bottom Sheet(半屏)
  • 触发:首次进入阅读页且未使用过“离线朗读”
  • 行为:
    • 主按钮:一键下载并朗读
    • 次级按钮:了解功能
    • 第三按钮:以后再说(不屏蔽功能,仅不再弹出本次)
    • 辅助:默认勾选“弱网或锁屏时自动离线朗读”(可取消)
  • 完成主按钮后,若开始播放 ≥15 秒且无错误,5–15 秒后以 SnackBar 提供微反馈(见2)。
  1. 播放中微反馈(轻量情绪评价)
  • 组件:SnackBar + Assist Chips(🙂 一般 / 👍 喜欢 / 👎 有问题)
  • 触发:
    • 播放累计 ≥2 分钟 或 单篇播放完成50%以上
    • 同一用户每周≤2次
  • 行为:
    • 点 👍:SnackBar闭合,提示“感谢反馈”;后台上报 csat=positive
    • 点 🙂 或 👎:打开“问题定位”半屏表单(见3)
  1. 问题定位半屏表单(可选附日志)
  • 组件:Modal Bottom Sheet(半屏)
  • 核心问题(多选,最多3项):
    • 朗读有停顿/卡顿
    • 发音不自然(语速/语调/标点/英文数字)
    • 耗电有点快
    • 被通知或其他音频打断
    • 倍速或进度不同步
    • 句子高亮错位
    • 蓝牙耳机控制不灵
    • 离线切换不稳定
    • 其他
  • 动态追问(选中后出现的简短子项,单选/开关,帮助定位):
    • 停顿/卡顿:发生在在线/离线/都发生;耳机有无;频率(偶尔/经常)
    • 发音不自然:问题偏向语速/停顿/英文读法/专有名词
    • 耗电:手机是否发热(是/否)
    • 打断:来源(来电/其他App音频/通知提示音/不确定)
    • 同步相关:不同步方向(声音快/高亮快)
    • 蓝牙:哪种操作失灵(播放/暂停/上一段/下一段/快进快退)
    • 离线切换:切换时刻(弱网/锁屏/后台)
  • 可选输入:补充说明(至多120字)
  • 可选开关(默认关闭):同意附带匿名诊断信息(播放会话指标与设备环境,不含个人内容)
  • 提交按钮:提交反馈
  • 提交后反馈:感谢 + “我们将在后续版本优化”;提供“查看已知问题与解决办法”链接
  1. 异常后快速标注
  • 音频被其他App打断后恢复播放时:顶部轻提示 Banner 2秒
    • 文案:“刚才被其他音频打断了吗?” 按钮:是 / 不是(是→直接上报 interrupt_confirmed=true;不是→上报false)
  • 自动切离线:SnackBar
    • 文案:“已切换为离线朗读(弱网/锁屏)。网络恢复后自动同步。” 动作:反馈(打开问题定位表单,入口定位至“离线切换不稳定”)
  1. 完成后轻量满意度
  • 文章朗读完成或播放≥70%后暂停退出时:
    • BottomSheet(紧凑):两行文本 + 3枚表情Chip(同2)
    • 每日不超过1次
  1. 常驻入口
  • 设置 > 朗读:列表项“问题反馈”
  • 播放通知(MediaStyle)扩展操作增加“问题反馈”
  • 阅读页溢出菜单:问题反馈

二、提示文案候选(可AB测试)

  • 首次引导(标题 + 说明 + 主按钮文案)
    • A款:离线朗读,弱网也流畅|一键下载文章,用系统朗读,支持蓝牙、倍速与句子高亮
    • B款:一键下载后就能边走边听|自动在弱网/锁屏时切离线,不中断
    • 主按钮:一键下载并朗读
    • 次级按钮:了解功能
    • 开关说明:弱网或锁屏时自动切离线
  • 播放中微反馈
    • 问:这次朗读体验如何?
    • 选项:👍 喜欢 / 🙂 一般 / 👎 有问题
  • 问题定位表单标题与描述
    • 标题:帮我们定位问题
    • 描述:选择遇到的情况(最多选3项)
    • 提交按钮:提交反馈
    • 附日志开关:同意附带匿名诊断(不含个人内容)
  • 异常相关
    • 被打断确认:刚才被其他音频打断了吗? 是/不是
    • 离线切换:已切换为离线朗读(弱网/锁屏)。网络恢复后自动同步。 反馈
  • 提交后
    • 感谢反馈,我们会尽快优化

三、组件样式(Material 3)

  • Modal Bottom Sheet(半屏)
    • 高度:内容自适应,最多占屏高60%
    • 圆角:Top 28dp
    • 阴影:Elevation 3
    • 背景:Surface颜色(支持动态色)
  • 主按钮:FilledButton,宽度充满,角半径12dp
  • 次级按钮:TextButton
  • 开关:Switch(带说明文案,触控区域≥48dp)
  • 选项:Checkbox/FilterChip(问题多选建议使用 FilterChip)
  • 表情反馈:AssistChip(含emoji与短文案,密度舒适,间距8dp)
  • SnackBar:带操作按钮(文字色对比度≥4.5:1)
  • Banner:ElevatedCard 顶部,自动消失2秒
  • 文本:Title Medium + Body Medium;面向普通用户,避免术语(如“音频焦点”→“其他音频”)

四、交互流程(文字版)

  • 首次进入 → 半屏引导 → 点击一键朗读 → 播放进行中 → 出现微反馈Chip → 若选一般/有问题 → 半屏问题定位 → 提交 → 感谢
  • 异常触发(被打断/离线切换)→ 小提示 → 快捷反馈或表单
  • 常驻入口触达 → 直接打开问题定位表单

五、频控与策略

  • 首次引导仅一次,用户点“以后再说”7天内不再弹
  • 播放中微反馈:每篇文章最多1次;每周最多2次
  • 异常确认Banner:单会话最多1次
  • 完成后满意度:每日最多1次
  • 远程配置可调阈值(播放时长、频控上限、触达开关)

六、无障碍与多设备适配

  • TalkBack顺序与语义标注;按钮含contentDescription
  • 最小触控区域≥48×48dp
  • 文案简短,支持动态字体缩放
  • 横竖屏、折叠屏与大屏优化;夜间模式对比度校验

技术实现

一、关键UI(Jetpack Compose示例)

  1. 首次引导半屏
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun OfflineTtsOnboardingSheet(
    show: Boolean,
    autoSwitchEnabled: Boolean,
    onAutoSwitchChange: (Boolean) -> Unit,
    onPrimaryClick: () -> Unit,
    onLearnMore: () -> Unit,
    onDismiss: () -> Unit
) {
    if (!show) return
    ModalBottomSheet(onDismissRequest = onDismiss) {
        Column(Modifier.padding(24.dp)) {
            Text("离线朗读,弱网也流畅", style = MaterialTheme.typography.titleMedium)
            Spacer(Modifier.height(8.dp))
            Text("一键下载文章,用系统朗读。支持蓝牙、倍速与句子高亮。",
                 style = MaterialTheme.typography.bodyMedium)
            Spacer(Modifier.height(16.dp))
            Row(verticalAlignment = Alignment.CenterVertically) {
                Switch(checked = autoSwitchEnabled, onCheckedChange = onAutoSwitchChange)
                Spacer(Modifier.width(8.dp))
                Text("弱网或锁屏时自动切离线")
            }
            Spacer(Modifier.height(24.dp))
            Button(onClick = onPrimaryClick, modifier = Modifier.fillMaxWidth()) {
                Text("一键下载并朗读")
            }
            TextButton(onClick = onLearnMore, modifier = Modifier.fillMaxWidth()) {
                Text("了解功能")
            }
            TextButton(onClick = onDismiss, modifier = Modifier.fillMaxWidth()) {
                Text("以后再说")
            }
        }
    }
}
  1. 播放中微反馈 SnackBar
@Composable
fun PlaybackFeedbackSnackbar(
    show: Boolean,
    onPositive: () -> Unit,
    onNeutral: () -> Unit,
    onNegative: () -> Unit
) {
    if (!show) return
    Snackbar {
        Row(
            horizontalArrangement = Arrangement.spacedBy(8.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text("这次朗读体验如何?")
            AssistChip(onClick = onPositive, label = { Text("👍 喜欢") })
            AssistChip(onClick = onNeutral,  label = { Text("🙂 一般") })
            AssistChip(onClick = onNegative, label = { Text("👎 有问题") })
        }
    }
}
  1. 问题定位半屏表单(要点)
data class IssueState(
    val selected: Set<String> = emptySet(),
    val followUps: Map<String, String> = emptyMap(), // e.g. "pause" -> "often"
    val note: String = "",
    val attachDiag: Boolean = false
)

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun IssueFormSheet(
    visible: Boolean,
    state: IssueState,
    onToggleIssue: (String) -> Unit,
    onFollowUpChange: (String, String) -> Unit,
    onNoteChange: (String) -> Unit,
    onAttachChange: (Boolean) -> Unit,
    onSubmit: () -> Unit,
    onDismiss: () -> Unit
) {
    if (!visible) return
    ModalBottomSheet(onDismissRequest = onDismiss) {
        Column(Modifier.padding(24.dp)) {
            Text("帮我们定位问题", style = MaterialTheme.typography.titleMedium)
            Text("选择遇到的情况(最多选3项)", style = MaterialTheme.typography.bodyMedium)
            Spacer(Modifier.height(12.dp))
            val options = listOf(
                "pause" to "朗读有停顿/卡顿",
                "pronunciation" to "发音不自然",
                "battery" to "耗电有点快",
                "interrupt" to "被通知或其他音频打断",
                "desync" to "倍速或进度不同步",
                "highlight" to "句子高亮错位",
                "bluetooth" to "蓝牙耳机控制不灵",
                "offlineSwitch" to "离线切换不稳定",
                "other" to "其他"
            )
            FlowRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                options.forEach { (key, label) ->
                    FilterChip(
                        selected = state.selected.contains(key),
                        onClick = { onToggleIssue(key) },
                        label = { Text(label) }
                    )
                }
            }
            // 示例:动态追问(以“被打断”为例)
            if ("interrupt" in state.selected) {
                Spacer(Modifier.height(12.dp))
                Text("可能来源", style = MaterialTheme.typography.labelLarge)
                val items = listOf("call" to "来电", "otherApp" to "其他App音频", "notif" to "通知提示音", "unknown" to "不确定")
                FlowRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                    items.forEach { (k, v) ->
                        AssistChip(
                            onClick = { onFollowUpChange("interrupt", k) },
                            label = { Text(v) },
                            leadingIcon = null,
                            enabled = true
                        )
                    }
                }
            }
            Spacer(Modifier.height(12.dp))
            OutlinedTextField(
                value = state.note,
                onValueChange = onNoteChange,
                placeholder = { Text("补充说明(可选,最多120字)") },
                singleLine = false,
                maxLines = 3,
                modifier = Modifier.fillMaxWidth()
            )
            Row(verticalAlignment = Alignment.CenterVertically) {
                Switch(checked = state.attachDiag, onCheckedChange = onAttachChange)
                Spacer(Modifier.width(8.dp))
                Text("同意附带匿名诊断(不含个人内容)")
            }
            Spacer(Modifier.height(16.dp))
            Button(onClick = onSubmit, modifier = Modifier.fillMaxWidth()) {
                Text("提交反馈")
            }
        }
    }
}
  1. 自动切离线提示与快捷入口
fun showOfflineSwitchSnackbar(
    scaffoldState: SnackbarHostState,
    onFeedback: () -> Unit
) {
    CoroutineScope(Dispatchers.Main).launch {
        val result = scaffoldState.showSnackbar(
            message = "已切换为离线朗读(弱网/锁屏)。网络恢复后自动同步。",
            actionLabel = "反馈",
            duration = SnackbarDuration.Short
        )
        if (result == SnackbarResult.ActionPerformed) onFeedback()
    }
}
  1. 通知栏入口(MediaStyle)
  • 在播放器通知的扩展动作中增加“问题反馈”Action,点击后启动 FeedbackActivity(解锁后打开)。
  • 建议使用 PendingIntent + FLAG_IMMUTABLE,避免隐式广播。

二、埋点指标定义(事件与参数)

统一要求:不收集PII;含会话ID(UUID)、时间戳、App版本、OS版本、机型、动态色/主题、屏幕方向。

  1. 功能引导
  • event: tts_offline_onboarding_impression
    • params: source=reading_page, auto_switch_default=true/false
  • event: tts_offline_onboarding_click
    • params: action=primary|learn_more|dismiss
  1. 播放与状态
  • event: tts_playback_start
    • params: session_id, article_id_hash, tts_engine, voice_locale, bluetooth_connected=true/false, network_type=wifi|cellular|none, start_mode=online|offline
  • event: tts_playback_progress
    • params: session_id, position_ms, buffered_ms, speed, highlight_enabled=true/false
    • 采样:每30s或关键节点(25/50/75%)
  • event: tts_offline_auto_switched
    • params: trigger=weak_network|screen_lock|background, success=true/false, latency_ms
  • event: tts_audio_focus_change
    • params: type=loss|loss_transient|duck|gain, app_in_foreground=true/false
  • event: tts_bluetooth_control
    • params: action=play|pause|next|prev|ff|rew, success=true/false
  1. 反馈相关
  • event: tts_feedback_snackbar_impression
    • params: trigger=progress|completion
  • event: tts_feedback_snackbar_click
    • params: option=positive|neutral|negative
  • event: tts_issue_form_impression
    • params: from=snackbar|settings|notification|offline_snackbar
  • event: tts_issue_submit
    • params:
      • issues=[pause,pronunciation,battery,interrupt,desync,highlight,bluetooth,offlineSwitch,other]
      • followups: map<string,string>(如 interrupt=call)
      • note_length
      • attach_diag=true/false
      • playback_summary: {duration_ms, avg_speed, offline_ratio, interrupt_count}
  • event: tts_interrupt_quick_label
    • params: confirmed=true/false
  1. 质量与能耗辅助(匿名)
  • event: tts_energy_snapshot
    • params: session_id, battery_level_start, battery_level_end, duration_ms, device_temp_bucket=cool|warm|hot
    • 说明:读取电量无需特殊权限,但频率受限:仅会话结束时上报

说明:

  • article_id以hash处理;不上传原文内容或朗读文本。
  • 若用户勾选“附带匿名诊断”,可附加:audio_focus_events_count、buffer_underrun_count、network_switch_count、tts_engine_name、voice_name、crash=false/true。

三、触发条件与策略建议

  • 微反馈触发:
    • 条件:(播放时长≥120s 或 完成度≥50%) 且 当周弹出次数<2
    • 屏蔽:Car Mode、通话中、DND优先(可由系统情景推断)
  • 异常确认Banner:
    • 条件:audio focus loss transient 后 ≤10s内自动恢复
    • 会话仅一次
  • 自动离线提示:
    • 条件:网络质量阈值低(可用带宽或重试次数)或锁屏事件
    • 同一会话最多2次提示

四、实现要点

  • 离线/在线切换判断
    • 监听Connectivity + 带宽估计,低于阈值触发离线下载与切换
    • 锁屏监听 ACTION_SCREEN_OFF → 切离线
  • 电量统计
    • 记录会话开始/结束的 BatteryManager.BATTERY_PROPERTY_CAPACITY
  • 蓝牙与耳机控制
    • MediaSession/AudioFocus正确处理,记录控制事件
  • 高亮与进度同步
    • 以句子ID为单位推进;将TTS回调 onRangeStart 或分句时机与UI高亮绑定;埋点 desync时延估算(UI到音频时间差)

注意事项

  • 隐私合规
    • 仅在用户明确勾选时上传匿名诊断;不采集文本内容、联系人、通话记录等PII
    • article_id做哈希;不上传原文标题
  • 性能与打扰控制
    • SnackBar/BottomSheet出现频率受控;播放中提示不可遮挡核心控制
    • 异常Banner自动消失,并不创建新层级焦点
  • 无障碍与本地化
    • ContentDescription补全;对表情Chip提供可读文本(喜欢/一般/有问题)
    • 文字对比度与动态字体适配
  • 通知与打断
    • 避免在驾驶模式或投屏演示时弹出干扰型反馈
    • MediaStyle操作数受限,确保“问题反馈”不影响核心控制(播放/暂停/前后)
  • QA清单
    • 首次引导频控与“以后再说”逻辑
    • 离线自动切换正确性(弱网/锁屏双触发)
    • 蓝牙耳机型号兼容性(播放/暂停/上一段/下一段/快进/快退)
    • 高亮同步在不同TTS引擎/语速下的对齐
    • 电量统计在不同厂商系统上的一致性
  • 评估指标(上线后)
    • 反馈参与率(微反馈点击率、表单提交率)
    • 正向满意度占比
    • 主要问题项占比(Top3变化趋势)
    • 与技术指标关联:buffer underrun与“停顿/卡顿”报错的相关性、audio focus loss与“被打断”的一致性
    • 功能N-Day留存、播放完成率变化

本方案围绕“首次引导—轻量反馈—问题定位—常驻入口—异常标注”的闭环设计,既保证了普通用户的低认知负担,又为研发提供了足够可落地的定位信息与可量化的指标,符合安卓Material 3规范与隐私合规要求。

场景分析

  • 使用人群与障碍
    • 老年用户,开启“老年模式:超大字体 + 高对比主题(或高对比文本)”
    • 常见问题:列表项文案被截断、按钮被遮挡、弹窗尺寸不适配(过小/内容不可见)
  • 触发环境与路径
    • 异常重现路径:设置 > 显示 > 字体大小(在此路径及其关键操作后最易触发)
    • 关键操作:调整字体滑杆、切换高对比主题、返回列表页/弹窗出现时
  • 约束与目标
    • 需“轻量反馈条”引导,支持“一键上传截图+设备信息”,附加语音输入入口
    • 文案清晰易懂,按钮足够大,颜色对比度达标,可与TalkBack/放大镜等辅助功能协作
    • 隐私合规:仅收集与显示问题强相关的技术信息;截图先预览,允许遮挡敏感信息

设计方案

1) 组件与版式

  • 反馈条(轻量、持久提示)

    • 位置:屏幕底部、紧贴内容区上方,避开底部导航;跟随系统/应用边距与手势区安全间距
    • 高度:56–64dp(保证≥48dp触控目标),圆角小、对比清晰
    • 内容结构:
      • 左侧:图标(alert/info),文案两行以内
      • 右侧:主按钮“立即反馈”,次要按钮“语音”,关闭“×”
    • 状态:进入“设置-显示-字体大小”页面或调节后即出现;同一会话最多出现2次,用户关闭后不再打扰
  • 底部浮层(反馈收集)

    • 样式:半高Modal BottomSheet,支持滑动展开;标题清晰,按钮堆叠式(纵向)
    • 步骤极简:最多3步完成提交
    • 功能:
      1. 问题类型单选(默认勾选“文案被截断/按钮遮挡/弹窗异常/其他”)
      2. 自动展示截图预览(可重新截取/涂抹遮挡)
      3. 简短问答选择题(异常排查问答)
      4. 语音输入(可替代文字描述)
      5. 提交按钮

2) 触发规则

  • 进入或返回以下页面/状态时触发:
    • 设置 > 显示 > 字体大小(页面展示后 1.2s,避免抖动)
    • 用户调整字体大小/高对比设置后,首次返回任意列表页、弹窗弹出时
  • 前提条件(任一满足):
    • fontScale ≥ 1.3(大字及以上)
    • 高对比文本/主题开启
  • 去重与频控:
    • 每次应用冷启动最多提示1次;用户主动关闭或已提交反馈后,本次会话不再弹出

3) 引导文案(对老年友好、明确具体)

  • 反馈条主文案(两行内,任选其一):
    • “如果文字看不全、按钮被遮挡,可一键反馈,帮您尽快修复。”
    • “遇到文字截断、按钮挡住、弹窗太小?点这里反馈,我们来修。”
  • 主按钮:
    • “立即反馈”
  • 次按钮(带麦克风图标):
    • “语音”
  • 关闭:
    • “稍后再说”
  • 底部浮层标题:
    • “请帮我们看看这个显示问题”
  • 问题类型(单选,默认选第1项):
    • “文字被截断”
    • “按钮被遮挡”
    • “弹窗显示异常”
    • “其他”
  • 描述输入占位:
    • “请简单说说哪里看不清/被挡住(可语音)”
  • 截图区:
    • “已为您截取当前页面。如有隐私,请点‘遮挡敏感信息’。”
  • 提交按钮:
    • “发送问题”
  • 成功Toast/条幅:
    • “已收到,我们会尽快修复。谢谢!”

4) 异常排查问答(最多4题,均为单选/是非,便于老年用户)

  • Q1 发生位置(自动识别当前页面,允许改选):
    • “就在当前页面”(默认)/“应用的设置页”/“其他页面”
  • Q2 是否影响操作:
    • “影响点击/看不清”/“不影响,只是不好看”
  • Q3 是否每次都会出现:
    • “每次都出现”/“偶尔出现”
  • Q4 字体/显示设置(勾选自动读取结果,用户确认即可):
    • “字体:特大/超大”
    • “高对比:已开启/未开启”
    • “显示大小:标准/大/更大”

5) 语音输入入口设计

  • 在反馈条和底部浮层内均提供“语音”按钮
  • 行为:调用系统语音识别(免学习成本)-> 转文字填入描述框 -> 用户可补充/直接提交
  • 失败与无权限:
    • 弹出简短提示:“无法使用麦克风,请在系统设置开启‘麦克风’权限。”

6) 字段采集(最小必要原则)

  • 基础
    • appVersionName/appVersionCode
    • activity/fragment 路径
    • 当前页面路由或标签(便于定位)
    • 时间戳、会话ID(匿名)
  • 设备显示与辅助信息(仅技术参数,无个人数据)
    • Android版本、厂商、机型
    • 屏幕分辨率、density、窗口insets(状态栏/导航栏高度)
    • fontScale、displaySize(显示大小)、isHighTextContrastEnabled、boldText/magnification/talkback启用状态
    • 深色/浅色模式、主题高对比开关(应用内)
    • 布局方向(LTR/RTL)、语言/地区
  • UI上下文
    • 列表项布局ID、按钮ID、弹窗类型
    • 当前弹窗宽高、是否可滚动
    • TextView/Compose Text的maxLines/ellipsize(可选)
  • 截图
    • 仅当前应用界面(不抓取通知/其他应用),本地预览后用户确认上传
    • 提供“遮挡敏感区域”工具(矩形涂抹/模糊)

7) 数据上传与失败兜底

  • 网络失败:本地暂存(加密)+ 重试;显式提示“已保存,将在网络恢复后发送”
  • 用户可取消:立即删除本地暂存

技术实现

以下示例以Jetpack Compose为主,附View系方案点到为止。

1) 反馈条(Compose)

@Composable
fun FeedbackBanner(
    visible: Boolean,
    onFeedbackClick: () -> Unit,
    onVoiceClick: () -> Unit,
    onClose: () -> Unit
) {
    AnimatedVisibility(visible = visible) {
        Surface(
            tonalElevation = 3.dp,
            color = MaterialTheme.colorScheme.surface,
            shadowElevation = 6.dp
        ) {
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .navigationBarsPadding()
                    .padding(horizontal = 16.dp, vertical = 12.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {
                Icon(Icons.Default.Info, contentDescription = null, tint = MaterialTheme.colorScheme.primary)
                Spacer(Modifier.width(12.dp))
                Text(
                    text = "如果文字看不全、按钮被遮挡,可一键反馈,帮您尽快修复。",
                    style = MaterialTheme.typography.bodyLarge, // 遵循大字号
                    modifier = Modifier.weight(1f)
                )
                Spacer(Modifier.width(8.dp))
                TextButton(onClick = onVoiceClick) {
                    Icon(Icons.Default.Mic, contentDescription = null)
                    Spacer(Modifier.width(4.dp))
                    Text("语音")
                }
                FilledTonalButton(onClick = onFeedbackClick) {
                    Text("立即反馈")
                }
                IconButton(onClick = onClose) {
                    Icon(Icons.Default.Close, contentDescription = "稍后再说")
                }
            }
        }
    }
}

触发条件示意:

val cfg = LocalConfiguration.current
val fontScale = cfg.fontScale
val highContrast = isHighTextContrastEnabled(context) || appHighContrastEnabled()
val shouldShowBanner = remember { mutableStateOf(false) }

LaunchedEffect(fontScale, highContrast) {
    shouldShowBanner.value = (fontScale >= 1.3f || highContrast) && notShownInThisSession()
}

高对比检测(示例):

fun isHighTextContrastEnabled(context: Context): Boolean =
    Settings.Secure.getInt(
        context.contentResolver,
        Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0
    ) == 1

2) 一键截图(仅应用内容,免系统录屏权限)

// build.gradle: implementation "androidx.core:core-ktx:<latest>"
fun captureCurrentScreen(activity: Activity): Bitmap {
    val view = activity.window.decorView.rootView
    return view.drawToBitmap() // 仅应用窗口内容
}

截图预览与遮挡:使用BottomSheet内简单涂抹工具(Canvas绘制半透明矩形并导出),或采用开源涂抹控件;导出时将遮挡层合成到bitmap后再压缩上传。

3) 反馈底部浮层(Compose)

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FeedbackSheet(
    screenshot: Bitmap,
    defaultOptions: FeedbackOptions,
    onSubmit: (FeedbackPayload) -> Unit,
    onRetake: () -> Unit,
    onVoiceClick: () -> Unit
) {
    ModalBottomSheet(onDismissRequest = { /* handle */ }) {
        Text("请帮我们看看这个显示问题", style = MaterialTheme.typography.titleLarge,
            modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp))

        // 问题类型
        val options = listOf("文字被截断", "按钮被遮挡", "弹窗显示异常", "其他")
        var selectedIndex by remember { mutableStateOf(0) }
        options.forEachIndexed { i, label ->
            Row(Modifier.fillMaxWidth().clickable { selectedIndex = i }.padding(16.dp)) {
                RadioButton(selected = selectedIndex == i, onClick = { selectedIndex = i })
                Spacer(Modifier.width(8.dp)); Text(label, style = MaterialTheme.typography.bodyLarge)
            }
        }

        // 截图预览
        Image(
            bitmap = screenshot.asImageBitmap(),
            contentDescription = "截图预览",
            modifier = Modifier
                .fillMaxWidth().heightIn(max = 260.dp)
                .padding(horizontal = 16.dp)
                .clip(RoundedCornerShape(8.dp))
        )
        Row(Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) {
            OutlinedButton(onClick = onRetake) { Text("重新截取") }
            Spacer(Modifier.width(8.dp))
            OutlinedButton(onClick = { /* 进入遮挡敏感信息工具 */ }) { Text("遮挡敏感信息") }
        }

        // 异常排查问答(简化)
        val q1 = listOf("就在当前页面", "应用的设置页", "其他页面")
        var q1Sel by remember { mutableStateOf(0) }
        LabeledSingleChoice("问题发生位置", q1, q1Sel) { q1Sel = it }

        val q2 = listOf("影响点击/看不清", "不影响,只是不好看")
        var q2Sel by remember { mutableStateOf(0) }
        LabeledSingleChoice("是否影响操作", q2, q2Sel) { q2Sel = it }

        val q3 = listOf("每次都出现", "偶尔出现")
        var q3Sel by remember { mutableStateOf(0) }
        LabeledSingleChoice("出现频率", q3, q3Sel) { q3Sel = it }

        // 描述 + 语音
        var desc by remember { mutableStateOf("") }
        OutlinedTextField(
            value = desc, onValueChange = { desc = it },
            placeholder = { Text("请简单说说哪里看不清/被挡住(可语音)") },
            modifier = Modifier
                .fillMaxWidth().padding(16.dp),
            minLines = 2, maxLines = 4
        )
        TextButton(
            onClick = onVoiceClick,
            modifier = Modifier.padding(horizontal = 16.dp)
        ) {
            Icon(Icons.Default.Mic, contentDescription = null); Spacer(Modifier.width(4.dp)); Text("语音")
        }

        // 提交
        Button(
            onClick = {
                onSubmit(
                    FeedbackPayload(
                        type = options[selectedIndex],
                        desc = desc,
                        q1 = q1[q1Sel], q2 = q2[q2Sel], q3 = q3[q3Sel],
                        // 其他自动采集字段在onSubmit中封装
                    )
                )
            },
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp)
                .heightIn(min = 48.dp)
        ) {
            Text("发送问题")
        }
        Spacer(Modifier.height(12.dp))
    }
}

辅助控件:

@Composable
fun LabeledSingleChoice(label: String, items: List<String>, selected: Int, onSelect: (Int) -> Unit) {
    Text(label, style = MaterialTheme.typography.titleMedium, modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp))
    items.forEachIndexed { i, txt ->
        Row(Modifier.fillMaxWidth().clickable { onSelect(i) }.padding(horizontal = 16.dp, vertical = 8.dp)) {
            RadioButton(selected = selected == i, onClick = { onSelect(i) })
            Spacer(Modifier.width(8.dp)); Text(txt, style = MaterialTheme.typography.bodyLarge)
        }
    }
}

4) 语音输入(系统识别,无需常驻权限UI)

fun startVoiceInput(activity: Activity, requestCode: Int = 1001) {
    val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
        putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
        putExtra(RecognizerIntent.EXTRA_PROMPT, "请说出问题位置或现象,例如“设置页文字被截断”")
    }
    try {
        activity.startActivityForResult(intent, requestCode)
    } catch (e: ActivityNotFoundException) {
        Toast.makeText(activity, "当前设备不支持语音输入", Toast.LENGTH_SHORT).show()
    }
}

// onActivityResult中接收结果,填入描述框

如需更自然地长语音,可使用SpeechRecognizer并申请RECORD_AUDIO权限;优先使用系统识别对话框以降低心智负担。

AndroidManifest(仅在自建识别需要时):

<uses-permission android:name="android.permission.RECORD_AUDIO"/>

5) 数据打包与上传

数据结构(示例):

{
  "type": "文字被截断",
  "desc": "设置-显示-字体大小页面,列表第2项看不全",
  "qa": {
    "where": "就在当前页面",
    "impact": "影响点击/看不清",
    "frequency": "每次都出现"
  },
  "app": {"versionName": "3.2.1", "versionCode": 30201},
  "device": {
    "brand": "Xiaomi", "model": "M2012K11AC", "android": "14",
    "density": 3.0, "widthPx": 1080, "heightPx": 2400,
    "fontScale": 1.6, "displaySize": "large",
    "highTextContrast": true, "boldText": false,
    "talkback": false, "nightMode": "light", "rtl": false, "locale": "zh-CN"
  },
  "ui": {"route": "Settings/Display/FontSize", "dialog": {"visible": false}},
  "screenshotId": "upload://abc123"
}

上传流程:

  • 截图文件先上传,返回ID,再发送表单JSON引用ID;失败则队列重试(WorkManager)

6) View体系快速集成要点

  • 反馈条可用Material Snackbar替代,但建议自定义“持久信息条”(避免Snackbar超时消失、字号较小问题)
  • 截图:rootView.drawToBitmap()
  • BottomSheet:BottomSheetDialogFragment + RecyclerView(选项)+ ImageView(截图)+ 自定义涂抹View
  • 无障碍:为按钮、图片设置contentDescription;对反馈条设置android:accessibilityLiveRegion="polite"

注意事项

  • 可用性与无障碍
    • 文案短、具体,避免术语;按钮≥48dp;点击目标间隔≥8dp
    • 颜色对比≥WCAG AA(文本对背景对比≥4.5:1),高对比主题下再加粗
    • TalkBack顺序:文案 -> 语音 -> 立即反馈 -> 关闭;反馈条设置liveRegion=polite避免打断读屏
  • 隐私与合规
    • 截图仅限应用内容;先预览、默认不上传;必须经用户确认
    • 提供“遮挡敏感信息”;不采集联系人、位置信息、输入内容等PII
    • 允许用户撤回/删除本地暂存
  • 性能与稳定
    • 截图/涂抹在后台线程压缩(PNG/JPEG/WebP),限制最大分辨率/文件大小
    • 频控与防打扰;出现时机避开关键交互(如输入中)
  • 兼容性
    • 字体缩放测试门槛:fontScale 1.0/1.3/1.6/2.0;深色/高对比/RTL
    • 老机型上BottomSheet动画卡顿时,允许无动画降级
  • 文案与本地化
    • 保持与品牌调性一致;简体中文为主,保留繁中与英文资源
  • 监控与度量
    • 埋点:反馈条曝光、点击率(立即反馈/语音/关闭)、提交完成率、失败率;用于后续优化

优先修复建议(针对问题根因,按修复价值排序)

  1. 文案截断

    • 禁用单行/强制省略:多处Text设置maxLines≥2, ellipsize尽量避免
    • 启用文本自适应:TextView使用android:autoSizeTextType="uniform";Compose中确保softWrap=true
    • 使用ConstraintLayout/Box,保留弹性空间;避免固定宽度/权重不当
  2. 按钮遮挡

    • 去除固定高度容器,改为wrapContent+padding,保证父容器可纵向滚动
    • 底部按钮区在小屏与大字体下允许自动“纵向堆叠”
    • 处理系统insets:WindowInsets.navigationBars.asPaddingValues(),避免被手势条/底部栏遮挡
  3. 弹窗尺寸异常

    • Dialog宽度:min(560dp, 屏宽的0.9),内容区可滚动(NestedScrollView/ModalBottomSheet)
    • 按钮从右到下自动换行;标题与内容允许多行
    • 避免在Dialog内嵌固定高度RecyclerView;改为wrapContent+maxHeight约束
  4. 主题与对比

    • 高对比主题下提升文本/主按钮对比度与fontWeight
    • 使用Material 3动态色的对比保护(contentColorFor + local contentAlpha)
  5. 预防性检测与测试

    • 在调试构建中启用“文本截断检测”日志:判断TextView.layout?.getEllipsisCount(line) > 0上报埋点(采样)
    • 回归测试覆盖fontScale 1.3/1.6/2.0;截图测试(Paparazzi/Compose Preview/宏基准)
    • 列表项避免左右双按钮并排;使用单主按钮+溢出菜单替代

此方案遵循Android/Material Design规范,面向老年用户提供清晰、低门槛的反馈体验;在不额外增加用户负担的前提下,最大化收集定位所需信息,便于快速复现与修复。

场景分析

  • 目标用户与状态

    • 用户画像:青少年用户,注意力窗口短、对打扰容忍度低,偏好轻量、直观、可控的交互;对隐私和“被跟踪”感敏感。
    • 使用情境:近30天未活跃用户回归,首次进入并完成“消息中心”浏览(列表停留与滚动达标)。此时用户可能对通知数量、相关性、打扰感更敏感,是收集满意度与优化点的最佳时机。
  • 反馈目标与范围

    • 反馈类型:满意度调查(非打断式)。
    • 维度聚焦(必须覆盖):通知频率感受、打扰感受、个性化推荐相关性。
    • 入口要求:消息列表顶部横幅(主入口)或“查看设置”后回到消息中心时的卡片(次入口)。
    • 行为选项:支持“跳过”“稍后提醒”,不影响当前浏览。
  • 约束与策略

    • 非打断式:不使用全屏弹窗/模态底部弹窗;采用列表内横幅/卡片,用户可继续滚动。
    • 交互时长控制:3题内完成,单题即点即提交;允许随时退出。
    • 优先级:中等 → 启用抽样、频控与曝光上限,不影响核心消息体验。
    • 隐私与合规:不收集开放文本,不索取联系方式等个人信息;仅记录选项与必要上下文(版本号、国家/语言、是否已调整通知设置等)。

设计方案

1) 触发条件与时机

  • 回归判定:last_active_at ≤ 当前时间 - 30天。
  • “首次完成消息中心浏览”定义(满足其一):
    • 在消息中心停留≥8秒;或
    • 滚动过≥5条消息;或
    • 发生一次“查看设置”入口点击后返回消息中心。
  • 展示时机:
    • 顶部横幅:用户满足浏览条件且停止滚动≥1.5秒时,横幅以轻微淡入+上滑出现;不占据全屏,不遮挡消息。
    • “查看设置”后卡片:用户从设置页返回消息中心时,在列表顶部显示卡片(若当次会话未展示过横幅)。
  • 频控与上限:
    • 抽样:默认50%用户(Remote Config可配 0–100%)。
    • 单会话最多展示1次。
    • “稍后提醒”:3天后且下次进入消息中心时再提示;最多重试1次(总曝光≤2),之后不再打扰。
    • “跳过/完成/关闭”后,本次会话内不再出现。

2) 信息架构与流程

  • 流程(主入口:顶部横幅)

    1. 横幅初始态(轻文案+行动) → 用户点击任一评分或“去填写”
    2. 展开为内嵌卡片(同位置) → 3题分步/渐进式,题目逐题出现
    3. 完成后显示感谢态,可提供“调整通知设置”与“管理推荐偏好”快捷入口
    4. 关闭或继续浏览
  • 流程(次入口:“查看设置”后卡片)

    • 从设置页返回后,在列表顶部显示同款卡片,用户可继续回答未完成项或直接完成/跳过。
  • 题目设计(3题,均为单选Chip,随选即存)

    1. 总体满意度(5点表情评分)
      • 很不满意 / 不太满意 / 一般 / 满意 / 很满意
    2. 通知频率感受(3选)
      • 太多了 / 刚刚好 / 有点少
    3. 打扰感受(3选)
      • 经常打扰我 / 偶尔打扰 / 几乎不打扰
    4. 个性化相关性(3选)
      • 很合我口味 / 一般般 / 不太相关
    • 注意:为确保问卷长度适配青少年,建议将第2/3/4题合并为两题方案:
      • 方案A(标准,3题):频率、打扰、相关性全收集(推荐默认)
      • 方案B(短版,2题):合并频率+打扰为一题(选项:太多且打扰/适中不打扰/较少且不打扰/不确定),再询问相关性(用于高敏捷场景)。通过Remote Config切换。
  • 行动与状态

    • 横幅右侧操作:稍后提醒、跳过、去填写
    • 卡片底部操作:返回/关闭、跳过、完成(按题目进度动态变化)
    • 完成感谢态:展示2个次要CTA(不自动跳转)
      • 调整通知设置
      • 管理推荐偏好(如有个性化推荐开关/兴趣管理页)

3) 分层文案(示例,可A/B测试)

  • 横幅(标题+副文案+行动)

    • 标题:帮我们把消息推送变得更合你
    • 副文案:30秒小问卷,不打断你看消息
    • 行动:去填写 | 稍后 | 跳过
    • 评分快捷入口(可选):5个表情图标(悬停提示:从“很不满意”到“很满意”)
  • 卡片·题1:总体满意度

    • 标题:最近的消息通知,你整体感觉如何?
    • 选项(表情+文字):很不满意 / 不太满意 / 一般 / 满意 / 很满意
  • 卡片·题2:通知频率

    • 标题:通知出现的频率对你来说?
    • 选项:太多了 / 刚刚好 / 有点少
  • 卡片·题3:打扰感受

    • 标题:它们会打扰到你吗?
    • 选项:经常打扰我 / 偶尔打扰 / 几乎不打扰
  • 卡片·题4:个性化相关性

    • 标题:这些通知和你兴趣相关吗?
    • 选项:很合我口味 / 一般般 / 不太相关
  • 完成感谢态

    • 标题:收到!我们会努力做得更好
    • 次要CTA:调整通知设置 | 管理推荐偏好
    • 关闭:继续浏览
  • 微文案规范

    • 避免“收集/追踪”等敏感词,用“改进体验”“更合你”表达。
    • 强调可控与不打扰:“不影响你看消息”“随时可跳过”。

4) 组件样式(Material Design 3)

  • 顶部横幅(Prominent Banner)

    • 容器:Container Color 使用色阶 Surface Variant(动态取材质你色),圆角8dp
    • 高度:wrap content,内边距16dp
    • 布局:左侧图标(Notifications/Bell,24dp),中部文案,右侧操作(AssistChip:去填写;TextButton:稍后、跳过)
    • 动效:淡入+上滑 200ms;出现延时1.5s;滑动时保持在列表内,不吸顶
  • 内嵌卡片(ElevatedCard)

    • 容器:Elevation 3,圆角12dp,内边距16dp
    • 标题:Title Medium;题目间距12–16dp
    • 选项:FilterChip/SegmentedButton(最少44dp触达高度,间距8dp)
    • 评分:5个表情IconButton(48dp最小触达,选中高亮)
    • 底部操作:TextButton(跳过)、FilledTonalButton(完成/继续)
    • 动效:问题渐显 150ms,选中状态有微动效(Scale 0.98→1.0,100ms)
  • 无障碍与青少年可用性

    • 字号:不小于Body Medium;支持系统字体缩放
    • 对比度:文字与背景对比≥4.5:1
    • 可聚焦:Chip/表情均有contentDescription
    • TalkBack:读出“问题X/共Y”“已选择XXX”
    • 手势:≥48dp触达面积

5) 频控与人群规则

  • 人群:仅“≥30天未活跃且本次为回归后首次进入消息中心”的用户
  • 黑名单:
    • 本版本已完成/跳过问卷用户
    • 近7天内修改过通知设置的用户(避免干扰)
  • 重试:仅针对“稍后提醒”,最多1次,间隔≥3天
  • 抽样与A/B:支持通过Remote Config控制展示比例、题目数量(2题/3题)、文案版本(A/B),默认50%

技术实现

1) 触发与状态管理

  • 本地持久化(DataStore)
    • keys:
      • lastActiveAt: Long
      • messageCenterFirstVisitAtAfterReturn: Long
      • surveyState: {NotEligible | Eligible | Shown | Started | Completed | Skipped | Snoozed}
      • surveyImpressionsCount: Int
      • snoozeUntil: Long
      • surveyVariant: {A,B}(Remote Config下发)
  • 触发逻辑(伪代码)
    • onEnterMessageCenter():
      • if now - lastActiveAt ≥ 30d && firstVisitAfterReturn && surveyNotCompleted:
        • if now ≥ snoozeUntil && impressions < 2 && inSample():
          • wait until user idle ≥1.5s OR scrolledItems ≥5 OR stay ≥8s
          • showBanner()

2) Jetpack Compose 组件示例(Kotlin)

@Composable
fun SurveyBanner(
    onStart: () -> Unit,
    onSnooze: () -> Unit,
    onSkip: () -> Unit,
) {
    ElevatedCard(shape = MaterialTheme.shapes.medium) {
        Row(
            Modifier.fillMaxWidth().padding(16.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Icon(Icons.Outlined.NotificationsActive, contentDescription = null)
            Spacer(Modifier.width(12.dp))
            Column(Modifier.weight(1f)) {
                Text("帮我们把消息推送变得更合你", style = MaterialTheme.typography.titleMedium)
                Text("30秒小问卷,不打断你看消息", style = MaterialTheme.typography.bodyMedium)
            }
            AssistChip(onClick = onStart, label = { Text("去填写") })
            TextButton(onClick = onSnooze) { Text("稍后") }
            TextButton(onClick = onSkip) { Text("跳过") }
        }
    }
}

@Composable
fun SurveyCard(
    step: Int,
    onAnswer: (questionId: String, answerId: String) -> Unit,
    onSkip: () -> Unit,
    onDone: () -> Unit
) {
    ElevatedCard(Modifier.fillMaxWidth(), shape = RoundedCornerShape(12.dp)) {
        Column(Modifier.padding(16.dp)) {
            when (step) {
                1 -> {
                    Text("最近的消息通知,你整体感觉如何?", style = MaterialTheme.typography.titleMedium)
                    Spacer(Modifier.height(12.dp))
                    Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                        val options = listOf("很不满意","不太满意","一般","满意","很满意")
                        options.forEachIndexed { idx, label ->
                            FilledIconButton(
                                onClick = { onAnswer("overall_satisfaction", (idx+1).toString()) }
                            ) { Text(label) } // 可替换为表情Icon
                        }
                    }
                }
                2 -> {
                    Text("通知出现的频率对你来说?", style = MaterialTheme.typography.titleMedium)
                    Spacer(Modifier.height(12.dp))
                    val opts = listOf(
                        "too_many" to "太多了",
                        "just_right" to "刚刚好",
                        "too_few" to "有点少"
                    )
                    FlowRow(mainAxisSpacing = 8.dp, crossAxisSpacing = 8.dp) {
                        opts.forEach { (id, label) ->
                            FilterChip(
                                selected = false,
                                onClick = { onAnswer("frequency", id) },
                                label = { Text(label) }
                            )
                        }
                    }
                }
                3 -> {
                    Text("它们会打扰到你吗?", style = MaterialTheme.typography.titleMedium)
                    Spacer(Modifier.height(12.dp))
                    val opts = listOf(
                        "often" to "经常打扰我",
                        "sometimes" to "偶尔打扰",
                        "rarely" to "几乎不打扰"
                    )
                    FlowRow(mainAxisSpacing = 8.dp, crossAxisSpacing = 8.dp) {
                        opts.forEach { (id, label) ->
                            FilterChip(
                                selected = false,
                                onClick = { onAnswer("disturbance", id) },
                                label = { Text(label) }
                            )
                        }
                    }
                }
                4 -> {
                    Text("这些通知和你兴趣相关吗?", style = MaterialTheme.typography.titleMedium)
                    Spacer(Modifier.height(12.dp))
                    val opts = listOf(
                        "high" to "很合我口味",
                        "medium" to "一般般",
                        "low" to "不太相关"
                    )
                    FlowRow(mainAxisSpacing = 8.dp, crossAxisSpacing = 8.dp) {
                        opts.forEach { (id, label) ->
                            FilterChip(
                                selected = false,
                                onClick = { onAnswer("personalization", id) },
                                label = { Text(label) }
                            )
                        }
                    }
                }
                5 -> {
                    Text("收到!我们会努力做得更好", style = MaterialTheme.typography.titleMedium)
                    Spacer(Modifier.height(8.dp))
                    Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
                        OutlinedButton(onClick = { /* open notification settings */ }) {
                            Text("调整通知设置")
                        }
                        OutlinedButton(onClick = { /* open personalization */ }) {
                            Text("管理推荐偏好")
                        }
                    }
                }
            }
            Spacer(Modifier.height(12.dp))
            Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
                TextButton(onClick = onSkip) { Text("跳过") }
                if (step in 1..4) {
                    FilledTonalButton(onClick = onDone) { Text("完成") }
                }
            }
        }
    }
}
  • 事件与上报(建议)

    • survey_impression:横幅/卡片曝光
    • survey_start:开始答题(点击“去填写”或选择第1题)
    • survey_question_answered:{qid, aid, step_index}
    • survey_skip:用户跳过
    • survey_snooze:用户稍后提醒
    • survey_complete:完成问卷(收集到目标维度)
    • survey_dismiss:关闭卡片/横幅
    • survey_entry_source:banner / after_settings
    • settings_click:从感谢态点击“调整通知设置”或“管理推荐偏好”
    • settings_change:通知设置/推荐偏好实际发生变更(监听结果)
    • session_context:app_version、locale、network、theme,仅用于聚合
  • 统计与转化口径(定义与公式)

    • 目标人群基数(Eligible Users):满足回归与首访条件且通过抽样与频控的用户
    • 展示率 = survey_impression / Eligible Users
    • 启动率 = survey_start / survey_impression
    • 题目完成率(Qx)= answered(qx) / survey_start
    • 问卷完成率 = survey_complete / survey_start
    • 跳过率 = survey_skip / survey_impression
    • 稍后率 = survey_snooze / survey_impression
    • 设置点击率 = settings_click / survey_complete
    • 设置变更转化率 = settings_change / settings_click
    • 满意度均值 = 平均(1–5分,整体满意度题)
    • 频率分布:太多/刚好/太少 各占比
    • 打扰分布:经常/偶尔/几乎不
    • 相关性分布:高/中/低
    • 后续行为指标(可选):
      • D1/D7 消息中心复访率(完成问卷 vs 未完成对照)
      • 通知开启率变化(问卷后7天)
    • A/B维度:题目数(2 vs 3)、文案A/B、入口来源(banner vs settings)
  • “稍后提醒”调度

    • 记录snoozeUntil = now + 3天
    • WorkManager/本地闹钟不主动打断,仅在下次进入消息中心并满足条件时复现
    • 重试计数+1,超过1次不再展示

3) 系统与导航

  • 从感谢态按钮跳转到:
    • 通知设置:Android 通知渠道设置页(Intent到App Notification Settings)或App内通知偏好页(更推荐,避免跳出App)
    • 推荐偏好页:App内个性化开关/兴趣管理
  • 支持深色模式与动态色;RTL 适配;横竖屏自适应
  • 列表集成:将横幅/卡片作为RecyclerView Header或Compose LazyColumn item(key="survey")

4) 性能与稳定性

  • 渲染:Compose Lazy加载,避免过度重组;图片/表情使用矢量Icon
  • 阻塞:不拦截手势,不抢焦点;出现/消失动画简短
  • 崩溃安全:事件上报做try-catch,不影响主流程

注意事项

  • 合规与隐私

    • 不采集自由文本、联系方式、地理位置等敏感数据
    • 明确可跳过与稍后,避免强制与暗诱导
    • 仅用于改进通知与推荐体验的匿名化汇总分析
  • 体验细节

    • 保持语气友好、简短,避免技术术语
    • 若系统已关闭通知,频率题可隐藏或替换为“你希望收到更多通知吗?”
    • 确保按钮顺序与平台习惯一致(主要行动在右侧)
  • 频控与干扰

    • 严格控制总曝光≤2;“稍后”仅一次
    • 当日运营Banner已存在时,延后本问卷至下次访问,避免信息堆叠
  • 可测试项(建议A/B)

    • 题目数(2题 vs 3题)对完成率与数据完整性的影响
    • 横幅文案A/B(强调“更合你” vs “不打扰”)
    • 评分组件(表情 vs 星级)对启动率的影响
  • QA清单

    • 首访判定边界(29天/30天临界)与跨时区
    • 从“查看设置”返回后卡片是否正确显示
    • TalkBack读屏顺序与焦点循环
    • 深色模式对比度与点击热区
    • 事件上报去重与序列一致性(start→answer→complete)

该方案遵循Material 3规范、满足非打断式与青少年友好要求,覆盖通知频率、打扰感与个性化相关性三大评价维度,并提供可落地的触发、组件、上报与转化口径定义,便于快速实现与迭代优化。

示例详情

解决的问题

为产品、设计、研发与运营团队提供一键生成的安卓应用内反馈提示方案,覆盖“新功能上线、关键操作完成、异常排查、用户召回与满意度调研”等核心场景;交付内容包括场景诊断、界面与交互节奏、组件与样式建议、文案候选与数据统计要点以及优先级与优化思路,帮助团队快速上线可用方案;以业务指标为导向,提升反馈参与率与有效率,降低跨部门沟通与反复返工成本,确保品牌一致与合规可控;支持按人群与情境定制,引导“在对的时机,用对的话术,向对的人提问”,加速产品持续改进与闭环。

适用用户

安卓产品经理

为新功能上线定制反馈弹窗与问卷,明确触发时机与用户分群;快速拿到可执行洞察,指导迭代优先级。

移动端UI/UX设计师

将反馈流程与界面统一到品牌风格,一键生成文案与交互草图,减少返工;确保提示不打断核心路径。

安卓开发工程师

直接获得实现建议与界面结构说明,明确事件触发与数据采集范围,降低沟通成本,快速落地。

特征总结

依据功能场景,一键生成反馈弹窗与文案,精准贴合用户当前任务,减少打扰感。
自动设计表单、评分、NPS等反馈形式,按优先级与触发时机智能分发。
内置引导话术与选项优化,减少输入负担,提升提交率与有效信息占比。
支持品牌语气与界面风格对齐,一键套用模板,保持一致且专业的体验。
结合用户行为与路径,智能选择展示时机,避免打断关键操作,降低流失。
自动输出实现建议与数据采集清单,缩短协同沟通时间,加速上线节奏。
内置多语言与本地化文案思路,轻松覆盖全球用户,降低翻译与适配成本。
提供A/B变体与评估维度建议,快速试验最佳提示方案,稳步提升留存与评分。
遵循隐私与平台规范,避免过度索取,降低合规风险,增强用户信任度。

如何使用购买的提示词模板

1. 直接在外部 Chat 应用中使用

将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。

2. 发布为 API 接口调用

把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。

3. 在 MCP Client 中配置使用

在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。

AI 提示词价格
¥20.00元
先用后买,用好了再付款,超安全!

您购买后可以获得什么

获得完整提示词模板
- 共 573 tokens
- 4 个可调节参数
{ 功能描述 } { 反馈类型 } { 目标用户 } { 优先级 }
获得社区贡献内容的使用权
- 精选社区优质案例,助您快速上手提示词
使用提示词兑换券,低至 ¥ 9.9
了解兑换券 →
限时半价

不要错过!

半价获取高级提示词-优惠即将到期

17
:
23
小时
:
59
分钟
:
59