Android 页面设计

Narrator Android 客户端 · 单 Activity + NavHostFragment · 16 个 Fragment

Narrator Android 客户端采用 单 Activity(MainActivity)+ Jetpack Navigation 架构。 所有页面均为 Fragment,通过 res/navigation/nav_graph.xml 集中声明, 由 NavController 统一调度。页面分为三类入口: 底部导航 侧边抽屉 子页面

📋 设计基线: v4 起移除 HomeFragmentstartDestination 改为 scriptFragment, 抽屉移除「主页」项。当前文档对应 docs/INDEX.md v5

页面总览

16 个 Fragment(不含基类 BaseFragment),按下表分组与入口方式归类:

# Fragment 文件 入口方式 参数
1ScriptFragmentui/fragments/ScriptFragment.java底部·剧本 / 启动入口
2ScriptDetailFragmentScriptDetailFragment.java点击剧本卡片scriptId
3NewChapterFragmentNewChapterFragment.java剧本内「添加章节」scriptId
4CharacterFragmentCharacterFragment.java底部·角色
5CharacterEditFragmentCharacterEditFragment.java点击系统角色charId
6ScriptCharactersFragmentScriptCharactersFragment.java剧本内「角色设置」scriptId
7ScriptCharacterEditFragmentScriptCharacterEditFragment.java点击剧本角色scriptId charName
8ScriptSettingsFragmentScriptSettingsFragment.java剧本内「设置」scriptId
9OutlineFragmentOutlineFragment.java剧本内「大纲」scriptId
10AiFragmentAiFragment.java底部·AI
11AiProviderDetailFragmentAiProviderDetailFragment.java点击 AI 服务商providerId
12DialogueFragmentDialogueFragment.java抽屉·对话
13DeductionFragmentDeductionFragment.java剧本内「演绎」scriptId role?
14UiFragmentUiFragment.java底部·UI
15TrashFragmentTrashFragment.java抽屉·回收站
16SettingsFragmentSettingsFragment.java底部·设置
17ManualFragmentManualFragment.java抽屉·手册
📌 参数读取约定: 所有带参数的 Fragment 在 onCreate(Bundle) 中通过 getArguments() 读取导航参数; 无参 Fragment 使用 BaseFragment.PLACEHOLDER_SCRIPT_ID(空字符串)占位。

Fragment 详情

1. ScriptFragment — 剧本列表

导航:启动入口 / 底部导航第 1 项 | 可跳转scriptDetailFragment

ScriptRepository.getInstance() 取数,按分组(悬疑/科幻/奇幻等)展示剧本卡片: 状态标签(进行中 / 草稿)+ 标题 + 描述 + 分组名。点击卡片进入 ScriptDetailFragment, 底部「添加剧本」按钮调用 ScriptRepository.add(...) 后刷新列表。

┌── 剧本 ──────────────────────────┐ ← 标题栏 ├──────────────────────────────────┤ │ 悬疑 │ ← 分组标题 │ ┌──────────────────────────┐ │ │ │ [进行中] 迷雾之城 │ │ ← 剧本卡片 │ │ 侦探在迷雾笼罩的城市中... │ │ │ │ 悬疑 │ │ │ └──────────────────────────┘ │ │ ... │ │ [+ 添加剧本] │ ← 底部按钮 └──────────────────────────────────┘

2. ScriptDetailFragment — 剧本详情

导航:从剧本列表点击卡片 | 可跳转outline / deduction / scriptCharacters / scriptSettings / newChapter

参数scriptId(String,必填,默认 PLACEHOLDER_SCRIPT_ID

ScriptRepository.getById(scriptId) 加载;标题与作者用 EditText, 通过 TextWatcher 实时回写 Repository。四个功能按钮(大纲 / 演绎 / 角色 / 设置)跳转子页面。

3. NewChapterFragment — 添加章节

入口:剧本详情点击「添加章节」 | 参数scriptId(String)

4. CharacterFragment — 角色管理

导航:底部导航第 2 项 | 上一级:HomeFragment | 可跳转characterEdit / scriptCharacters

双 Tab 设计:Tab「系统角色」点击跳转 CharacterEditFragment;Tab「自定义角色」展开剧本后点击跳转 ScriptCharactersFragment

5. CharacterEditFragment — 角色详情编辑

入口:角色管理点击系统角色 | 参数charId(String)

6. ScriptCharactersFragment — 剧本角色管理

入口:剧本详情 / 角色管理 | 参数scriptId可跳转scriptCharacterEdit

7. ScriptCharacterEditFragment — 剧本角色编辑

参数scriptId(String)、charName(String)

8. ScriptSettingsFragment — 剧本设置

入口:剧本详情点击「设置」 | 参数scriptId

9. OutlineFragment — 大纲编辑

入口:剧本详情点击「大纲」 | 参数scriptId

对应 Web OutlinePage.vue。数据通过 OutlineRepository 持久化到 {filesDir}/outline/{scriptId}/outline.json(卷/章目录树)。

10. AiFragment — AI 服务商配置

导航:底部导航第 3 项 | 上一级:HomeFragment | 可跳转aiProviderDetail

左右双面板布局:左 .left_panel 展示 AI 服务商列表(来自 AIProviderRepository → Room ai_providers 表),右 .right_panel 为配置面板占位。

11. AiProviderDetailFragment — AI 服务商详情

入口:点击 AI 服务商卡片 | 参数providerId

编辑 AIProviderEntity 的 apiUrl / apiKey / modelName 等字段,通过 fetchModels 查询 API 支持的模型列表。

12. DialogueFragment — 对话

导航:左侧抽屉 | 对应:Web DialoguePage.vue

聚合展示跨剧本对话历史,数据来自 Room dialogues 表(DialogueEntity)。

13. DeductionFragment — 剧本推演

入口:剧本详情点击「演绎」 | 参数scriptId(必填)、role(可选)

三栏布局.left_panel(72dp,角色栏)+ .center_panel(weight:1,对话区)+ .right_panel(72dp,信息面板)。

.page { row } ├── .left_panel { width:72dp } ← 角色栏 ├── .center_panel { weight:1 } ← 对话流 └── .right_panel { width:72dp } ← 信息面板

演绎三阶段循环

该 Fragment 是 Narrator 的核心,实现「发言顺序链」模式(见 DeductionFragment.java):

🎬 阶段 1 · 导演决策: 根据大纲 + 角色 + 历史消息生成指令,指令包含本回合的发言顺序链阶段 2 · 角色依次发言: 按导演制定的顺序链逐个发言——人类角色等待用户输入,AI 角色调用 AiService.sendChatStream(...) 自动生成。 阶段 3 · 旁白收尾: 回合结束时生成场景收尾描述,完成一轮并进入下一回合。

每个角色的完整数据(CharData)含 id、name、avatar、player(character/system)、personality、goal、relationships、abilities、temperature、status(在线/不在线/连线)。世界观描述从 OutlineRepository.load(scriptId).worldview 加载。

14. UiFragment — UI 主题配置

导航:底部导航第 4 项 | 对应:Web UiPage.vue

通过 ThemeEngine 单例管理 8 个内置色彩主题;主题数据持久化到 SharedPreferences("carrot_theme")。 页面展示主题卡片网格,点击即切换;底部展示当前主题的 16 色色板。详见 主题引擎文档

ScrollView └── .page { padding:16dp } ├── TextView "UI" ├── .tabs { row } │ ├── "风格" │ ├── "版式主题" │ └── "导入" └── .demo_card { card-bg } ├── "演示效果" └── "(主题预览占位)"

15. TrashFragment — 回收站

导航:左侧抽屉 | 对应:Web TrashPage.vue

展示被删除的剧本列表(TrashRepository),支持恢复。删除剧本时 ScriptRepository.remove(id) 会先把剧本移入回收站。

16. SettingsFragment — 应用设置

导航:底部导航第 5 项 | 对应:Web SettingsPage.vue

含「演绎设置」与「配置管理」两个 section。默认值统一从 res/values/defaults.xmlutil/Defaults 读取(speechMode / autoSave / engineMode / defaultModel / fontFamily / fontSize 等)。

ScrollView └── .page { padding:16dp } ├── TextView "设置" ├── .section_deduction { card-bg } │ ├── "演绎设置" │ └── "(设置项占位)" └── .section_config { card-bg } ├── "配置管理" └── "(导入/导出占位)"

17. ManualFragment — 使用手册

导航:左侧抽屉「手册」项 | 参数:无

应用内置使用手册页,与「对话」「回收站」同为抽屉入口的顶层页面。

代码结构

java/com/carrots/narrator/ ├── CarrotSApp.java # Application(崩溃捕获 + 日志初始化) ├── MainActivity.java # 主壳(底部导航 + 抽屉 + 标题栏) ├── ui/fragments/ │ ├── BaseFragment.java # 基类(导航辅助方法) │ ├── ScriptFragment.java # 剧本列表 │ ├── ScriptDetailFragment.java # 剧本详情 │ ├── NewChapterFragment.java # 添加章节 │ ├── CharacterFragment.java # 角色管理 │ ├── CharacterEditFragment.java # 角色编辑 │ ├── ScriptCharactersFragment.java │ ├── ScriptCharacterEditFragment.java │ ├── ScriptSettingsFragment.java │ ├── OutlineFragment.java # 大纲编辑 │ ├── AiFragment.java # AI 配置 │ ├── AiProviderDetailFragment.java │ ├── DialogueFragment.java # 对话 │ ├── DeductionFragment.java # 演绎(三栏 + 三阶段) │ ├── UiFragment.java # UI 主题 │ ├── TrashFragment.java # 回收站 │ ├── SettingsFragment.java # 设置 │ └── ManualFragment.java # 手册 ├── model/ # 数据层(Room + 文件仓库) ├── service/AiService.java # AI 调用 ├── theme/ # 主题引擎 └── util/ # 工具类

关键约束

  1. 页面只能通过 NavController 导航,禁止直接启动 Activity。
  2. 底部导航 5 个 Tab 为一级页面,切换不压栈(popUpTo(scriptFragment, false) + launchSingleTop(true))。
  3. 抽屉页面同样为一级页面(对话 / 手册 / 回收站)。
  4. 子页面(剧本详情 / 大纲 / 演绎等)通过 navController.navigate() 入栈,支持返回。
  5. 标题栏按钮显隐:子页面显示 ,顶层页面显示 (由 MainActivity.isSubPageDest() 判定)。
  6. 标题栏文字自动取 NavDestination.getLabel()
  7. 底部导航高亮与底部选中指示条颜色跟随当前主题,由 ThemeColorsUtil.get(...) 动态应用。
  8. 演绎页面特殊:同时显示返回按钮与 菜单按钮,并隐藏底部导航及指示条。

页面变更记录

日期版本变更内容
2026-07-05v5创建 Script 模型 + ScriptRepository(5 预置示例剧本 + CRUD);ScriptFragment 改为分组展示剧本卡片;ScriptDetailFragment 标题/作者改为 EditText + TextWatcher 实时回写。
2026-07-05v4移除 HomeFragment 与 hero_box,startDestination 改为 scriptFragment,抽屉移除「主页」项。新增 ThemeEngine + 实装 UiFragment 6 色主题卡片网格。
2026-07-05v3修复回退栈膨胀:navigateTo() 增加 NavOptionslaunchSingleTop=true + popUpTo=scriptFragment, false)。
2026-07-05v2新增标题栏返回按钮;scriptId 全部去硬编码为 BaseFragment.PLACEHOLDER_SCRIPT_ID;多 Fragment 修复 onCreate 参数读取;抽屉高亮同步。
2026-07-04v1初始设计文档。