WebToApp 应用分类Categories完全指南基于 My Apps 主屏幕的分类组织、筛选与移动操作导读本文围绕 WebToApp 主屏幕My Apps中顶栏下方的分类标签行Category Tabs系统讲解应用分类的完整用法如何创建带名称与图标的分类、如何编辑与删除、如何把已有应用一键移动到目标分类以及分类筛选在底层是如何被持久化与索引的。读完本文你将掌握用分类把大量 Web、Node.js、Python、PHP、WordPress 等项目有序组织起来的工作流并理解分类纯组织、不影响构建这一设计原则背后的数据模型依据。一、分类在界面中的位置与作用分类标签行位于 My Apps 主屏幕顶栏top bar的正下方与顶栏中的标题、深色/浅色切换、语言切换、搜索 和 更多⋮ 入口同屏协作。它的作用是把屏幕主体区域的 应用列表 按分类维度切分让用户在海量 App 卡片中快速定位目标。从布局结构看参见 My Apps 中的示意┌─────────────────────────────────┐ │ My Apps ⋮ │ ← 顶栏 ├─────────────────────────────────┤ │ All │ Work │ Demo │ │ ← 分类标签行本文主题 ├─────────────────────────────────┤ │ ┌─────────────────────────────┐ │ │ │ Example https://… │ │ ← 应用列表按选中分类过滤 │ │ [Web] [AdBlock] ⋮ │ │ │ └─────────────────────────────┘ │ │ … │ ├─────────────────────────────────┤ │ Create │ ← 创建按钮 └─────────────────────────────────┘选择一个分类标签即可把下方的 应用列表 过滤为该分类下的应用标签行最右侧的操作用于添加新分类。二、分类的基础操作添加、编辑与删除1. 添加分类点击分类标签行右侧的按钮弹出创建对话框需要提供两项内容字段说明默认值名称name分类的显示名称将直接展示在标签上必填图标icon分类的展示图标源码默认从数据模型看一个分类并不只有这两个字段。在 AppCategory.kt 中分类被建模为 Room 实体映射到app_categories表Entity(tableName app_categories) data class AppCategory( PrimaryKey(autoGenerate true) val id: Long 0, val name: String, val icon: String , val color: String #6200EE, val sortOrder: Int 0, val createdAt: Long System.currentTimeMillis() )也就是说每个分类除了 UI 上可见的name与icon还在底层维护了id自增主键作为 WebApp 外键关联的标识color主题色默认#6200EE用于标签的视觉区分sortOrder排序权重用于控制标签的先后顺序createdAt创建时间戳作为同排序权重下的次级排序依据。2. 编辑分类对一个已有分类可以随时重命名或更换图标。编辑操作最终落在 DAO 层的更新方法上AppCategoryDao.kt 提供了完整的数据访问接口Query(SELECT * FROM app_categories ORDER BY sortOrder ASC, createdAt ASC) fun getAllCategories(): FlowListAppCategory Query(SELECT * FROM app_categories WHERE id :id) suspend fun getCategoryById(id: Long): AppCategory? Insert(onConflict OnConflictStrategy.REPLACE) suspend fun insert(category: AppCategory): Long Update suspend fun update(category: AppCategory) Delete suspend fun delete(category: AppCategory) Query(DELETE FROM app_categories WHERE id :id) suspend fun deleteById(id: Long) Query(SELECT COUNT(*) FROM app_categories) suspend fun getCategoryCount(): Int值得注意的细节是列表查询语句ORDER BY sortOrder ASC, createdAt ASC——标签的排列顺序严格由sortOrder升序决定相同排序权重时再按创建时间先后排列而不是按名称或创建时间。这一点可以指导你设置自定义分类的展示次序。3. 删除分类删除一个不再需要的分类后该分类标签会从标签行消失。底层由deleteById完成物理删除数据仓库 AppCategoryRepository.kt 对 DAO 做了薄封装供 UI 层调用class AppCategoryRepository(private val categoryDao: AppCategoryDao) { val allCategories: FlowListAppCategory categoryDao.getAllCategories() suspend fun getCategoryById(id: Long): AppCategory? categoryDao.getCategoryById(id) suspend fun createCategory(category: AppCategory): Long categoryDao.insert(category) suspend fun updateCategory(category: AppCategory) categoryDao.update(category) suspend fun deleteCategory(category: AppCategory) categoryDao.delete(category) suspend fun deleteCategoryById(id: Long) categoryDao.deleteById(id) suspend fun getCategoryCount(): Int categoryDao.getCategoryCount() }分类列表以Flow形式暴露UI 侧订阅后即可在新增/编辑/删除发生时自动刷新标签行。三、把应用移动到分类Move to Category分类对应用的影响通过WebApp实体上的categoryId字段建立关联。在 WebApp.kt 中应用实体声明了val categoryId: Long? null并在表级别为categoryId建立了索引Index(value [categoryId])保证按分类过滤应用时的高效查询。移动应用的操作路径为在应用卡片上点击右上角⋮打开该 App 的 操作菜单选择Move to Category参见 Move to Category在弹出的对话框中列表会列出当前所有分类应用当前所属的分类会被标记出来点选目标分类后应用立即移动应用列表随即按新分类重新过滤。关于移动操作官方文档明确了两条行为准则分类是**纯组织性purely organizational**的——移动应用不会改变它的构建方式或运行方式构建出的 APK 行为不受分类影响分类的新增与编辑统一从分类标签行入口完成移动对话框本身只负责选择目的地这一件事。四、分类筛选的底层实现三态选择与持久化分类标签行在交互上支持三种选择状态All全部、Uncategorized未分类、以及某个具体分类。这一逻辑被封装在 CategoryFilterStore.kt 中其设计很有参考价值class CategoryFilterStore(context: Context) { private val prefs context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) var rememberEnabled: Boolean get() prefs.getBoolean(KEY_REMEMBER, true) set(value) prefs.edit().putBoolean(KEY_REMEMBER, value).apply() /** Saved selection, or null for All / no record. */ fun loadSelection(): Long? { if (!prefs.contains(KEY_SELECTED)) return null return when (val v prefs.getLong(KEY_SELECTED, VALUE_ALL)) { VALUE_ALL - null else - v } } fun saveSelection(categoryId: Long?) { prefs.edit().putLong(KEY_SELECTED, categoryId ?: VALUE_ALL).apply() } private companion object { const val PREFS_NAME home_category_filter const val KEY_REMEMBER remember_enabled const val KEY_SELECTED selected_category const val VALUE_ALL Long.MIN_VALUE } }几个值得展开的实现细节三态编码为单个 Long选择状态被压缩进一个Long——Long.MIN_VALUEVALUE_ALL表示全部-1表示未分类与 UI 侧使用的值一致其余任意正数表示具体的分类id。注释中特别说明缺失键也等价于全部因此全新安装与记录被清除后的行为完全一致不会出现空指针式的默认态混乱。持久化与恢复开关解耦rememberEnabled开关只控制启动时是否恢复上次选择而saveSelection始终写入当前选择。这意味着即使你先关闭了恢复开关之后再打开仍然能恢复到最近一次的选择——写入从不间断开关只决定读取时机。选择即写每一次切换分类标签都会立即落盘apply()异步写入 SharedPreferences配合Flow驱动的分类列表形成了点标签 → 过滤列表 → 记忆选择的完整闭环。五、实践建议与注意事项综合文档与源码整理出以下可直接落地的使用要点命名策略分类名会直接展示为标签建议使用简短、可区分的名称如 Work、Demo、Personal配合图标与主题色color字段形成视觉辨识度排序控制通过sortOrder调整标签排列顺序同权重时按创建时间升序新增分类默认排在同类权重之后善用未分类状态删除分类后原属于该分类的应用的categoryId置空可在 Uncategorized 状态下统一查看与重新归置分类不影响产物牢记分类纯组织、不影响构建运行的原则——把按项目类型归类与按使用频率归类结合使用不必担心分类会影响 APK 导出行为相关导出流程可参考 APK 导出与搜索配合分类过滤与 搜索 是叠加关系分类先粗筛、搜索再精筛适合应用数量较多时的快速定位。六、总结WebToApp 的应用分类是一套轻量但完整的组织体系UI 层通过顶栏下方的标签行提供选择、添加、编辑、删除四类交互数据层以app_categories表AppCategory实体承载名称、图标、颜色、排序权重与创建时间以web_apps.categoryId带索引建立应用与分类的关联筛选状态则由CategoryFilterStore以三态 Long 编码并持久化到 SharedPreferences。无论你是维护几十个测试项目的老玩家还是刚起步的新手这套机制都能让 My Apps 主屏幕保持整洁、可检索且完全不影响任何 App 的构建与运行。创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考