尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

【细胞工坊|08】HarmonyOS ArkTS 知识点列表实战:实现细胞、遗传与病毒知识分类

【细胞工坊|08】HarmonyOS ArkTS 知识点列表实战:实现细胞、遗传与病毒知识分类 部分内容由AI辅助生成。本文面向 HarmonyOS 5.0 及以上版本基于 细胞工坊 项目真实源码展开源码根目录为D:\huawei\one14-9。本文重点复核entry/src/main/ets/pages/LearningPage.ets、entry/src/main/ets/views/learning/KnowledgeListPage.ets、entry/src/main/ets/views/learning/KnowledgeDetailPage.ets与entry/src/main/ets/model/KnowledgePoint.ets。文章只讨论源码已经实现的分类入口、知识点过滤、颜色图标映射、列表卡片、详情页参数传递和安全区适配不虚构搜索、收藏、学习进度同步、远程知识库或 AI 推荐。知识型页面最容易写成“静态内容堆叠”首页给几个入口点进去列一堆标题再点开看正文。短期看能交付但分类、详情、图标、颜色和路由参数如果没有边界后面增加细胞、遗传、病毒、DNA、培养等主题时页面会迅速变得难维护。细胞工坊的实现采用了一个比较稳的链路学习中心通过分类卡片传入category知识点列表根据分类过滤本地数组列表项按知识点 ID 映射图标点击后把知识点字段传给详情页。这篇文章的重点不是讲生命科学知识本身而是拆解 HarmonyOS ArkTS 页面如何把知识分类做成可复核、可扩展的本地信息架构。当前源码中的知识点来自getAllKnowledgePoints()静态数组分类信息来自getKnowledgeCategories()没有网络请求也没有后台内容管理。这个边界对 AppGallery 审核和离线教学定位都很重要。一、先看入口学习中心负责分类网格不负责列表过滤LearningPage是学习中心它读取知识分类和学习工具Component export struct LearningPage { State categories: KnowledgeCategory[] getKnowledgeCategories() State tools: LearningTool[] getLearningTools() State screenWidth: number 360 }分类入口不是硬编码在 UI 里而是由KnowledgePoint.ets提供export interface KnowledgeCategory { name: string count: number icon: Resource color: string }点击分类卡片时学习中心只做一件事跳转到知识点列表页并传入分类名。router.pushUrl({ url: views/learning/KnowledgeListPage, params: { category: cat.name } })这个边界比较清楚。学习中心不需要知道该分类下有哪些知识点也不负责筛选数组。它只负责展示分类入口。真正的过滤逻辑留给KnowledgeListPage这样入口页不会被列表规则污染。二、分类网格做了响应式列数适合手机和平板学习中心的分类网格不是固定两列而是根据屏幕宽度返回不同列模板private gridCols(): string { if (this.screenWidth 840) return 1fr 1fr 1fr 1fr if (this.screenWidth 600) return 1fr 1fr 1fr return 1fr 1fr }根容器通过onAreaChange更新宽度.onAreaChange((_o, n) { this.screenWidth n.width as number })Grid 使用这个模板Grid() { ForEach(this.categories, (cat: KnowledgeCategory) { GridItem() { Column() { Image(cat.icon) Text(cat.name) Text(${cat.count}个知识点) } } }) } .columnsTemplate(this.gridCols()) .columnsGap(10) .rowsGap(10)对 HarmonyOS 多设备应用来说这种做法比写死两列更稳。手机上两列可读平板或 2in1 窗口变宽后可以扩展到三列或四列分类卡片不会被拉得过宽。三、知识点模型只存可展示字段知识点数据结构很轻export interface KnowledgePoint { id: string title: string category: string summary: string content: string }它没有收藏状态、学习进度、远程 ID、版本号或富媒体 URL。当前源码定位就是一个本地静态知识库。列表页和详情页都围绕这五个字段展开。分类统计则由KnowledgeCategory提供export function getKnowledgeCategories(): KnowledgeCategory[] { return [ { name: 基础, count: 12, icon: $r(app.media.ic_bio_microscope), color: #00D9FF }, { name: 培养, count: 10, icon: $r(app.media.ic_bio_petri), color: #00FFB2 }, { name: DNA, count: 9, icon: $r(app.media.ic_bio_dna), color: #7C4DFF }, { name: 病毒, count: 8, icon: $r(app.media.ic_bio_virus), color: #FF4D4F }, { name: 遗传, count: 8, icon: $r(app.media.ic_bio_genetics), color: #7C4DFF }, { name: 细胞, count: 10, icon: $r(app.media.ic_bio_mitosis), color: #00FFB2 } ] }命令行读取KnowledgePoint.ets时部分中文可能显示为乱码但分类结构、字段和数量可以从源码函数复核。文章只基于这些可验证字段描述分类能力。四、列表页从路由参数读取分类KnowledgeListPage定义了路由参数接口interface KnowledgeListParams { category?: string }页面初始化时读取参数aboutToAppear(): void { const params router.getParams() as KnowledgeListParams | undefined if (params?.category) { this.category params.category } this.reloadPoints() }这段代码有两个工程点逻辑作用风险控制params?.category支持从学习中心传入分类参数不存在时保留默认分类reloadPoints()统一刷新列表数据避免 UI 树里写过滤逻辑默认分类是基础State category: string 基础因此即使页面不是从分类网格进入而是从首页快捷入口进入也能展示基础知识点列表不会出现空白页。五、过滤逻辑带基础兜底避免未知分类导致空列表知识点过滤函数很短但有明确兜底private reloadPoints(): void { const filtered this.allPoints.filter((item: KnowledgePoint) item.category this.category) this.points filtered.length 0 ? filtered : this.allPoints.filter((item: KnowledgePoint) item.category 基础) }如果传入的分类存在就展示该分类如果传入分类无匹配结果就退回基础分类。这样做可以防止路由参数拼错、旧版本入口残留或外部快捷入口传入未知分类时页面空白。这里有一个边界页面标题仍显示this.category · 知识点如果传入未知分类列表显示基础内容但标题可能仍是未知分类。后续如果要更严谨可以在兜底时同步修正categoryif (filtered.length 0) { this.points filtered } else { this.category 基础 this.points this.allPoints.filter((item: KnowledgePoint) item.category 基础) }这只是可改进方向当前源码的真实实现是“列表数据兜底到基础标题不主动重置”。六、分类颜色用函数集中映射列表页根据当前分类返回主色private getCategoryColor(): string { const cat this.category if (cat 基础) return #00D9FF if (cat 培养) return #00FFB2 if (cat DNA) return #7C4DFF if (cat 病毒) return #FF4D4F if (cat 遗传) return #7C4DFF if (cat 细胞) return #00FFB2 return #00D9FF }这个函数让列表项图标、数字标牌和分类视觉保持一致。比在每个组件里手写颜色更容易维护。但这里也有一个可维护性问题颜色在getKnowledgeCategories()和getCategoryColor()中各写了一份。后续如果分类色调整可能出现入口页和列表页颜色不一致。更稳的方式是把分类颜色统一放在一个配置表里然后学习中心和列表页都读取同一份数据。七、知识点图标按 ID 映射不只按分类粗略映射getPointIcon()是当前页面较长的一段逻辑。它不是简单按分类给同一个图标而是按知识点 ID 映射更具体的资源private getPointIcon(kp: KnowledgePoint): Resource { const id kp.id if (id basic_1) return $r(app.media.ic_bio_microscope) if (id basic_2) return $r(app.media.ic_bio_cell) if (id basic_3) return $r(app.media.ic_bio_protocol) if (id basic_4) return $r(app.media.ic_bio_pipette) if (id basic_5) return $r(app.media.ic_bio_shield) if (id culture_1 || id culture_6 || id culture_7) { return $r(app.media.ic_bio_petri) } if (id dna_3 || id dna_4 || id dna_5) { return $r(app.media.ic_bio_dna) } return $r(app.media.ic_bio_cell) }这能让知识点列表更像知识库而不是同一分类下所有条目都长一样。比如 DNA 提取、PCR、突变、离心分层可以显示不同图标用户扫列表时更容易区分主题。代价是函数会越来越长。当前数据规模大约几十条手写映射还能接受如果知识点继续增长建议把图标名放进KnowledgePoint数据结构或者建立Recordstring, Resource映射表。八、列表卡片做了长文本保护知识点列表使用List和ForEach渲染List({ space: 8 }) { ForEach(this.points, (kp: KnowledgePoint, index: number) { ListItem() { Row() { // icon text arrow } } }) } .width(100%) .layoutWeight(1)标题和摘要都限制了行数Text(kp.title) .fontSize(AppFonts.BODY_SIZE) .fontWeight(AppFonts.WEIGHT_MEDIUM) .fontColor(AppColors.TEXT_PRIMARY) .maxLines(1) .textOverflow({ overflow: TextOverflow.Ellipsis }) Text(kp.summary) .fontSize(AppFonts.CAPTION_SIZE) .fontColor(AppColors.TEXT_SECONDARY) .margin({ top: 4 }) .maxLines(2) .textOverflow({ overflow: TextOverflow.Ellipsis })这对知识内容页很重要。知识点摘要天然会比功能按钮文案长如果不做截断小屏设备上列表高度会失控右侧箭头也可能被挤出可视区域。九、数字标牌提供当前分类内的阅读顺序列表项左侧使用Stack组合背景圆、图标和数字Stack({ alignContent: Alignment.BottomEnd }) { Column() {} .width(42) .height(42) .borderRadius(21) .backgroundColor(this.getCategoryColor()) .opacity(0.12) Image(this.getPointIcon(kp)) .width(22) .height(22) .fillColor(this.getCategoryColor()) .alignSelf(ItemAlign.Center) Text(${index 1}) .fontSize(8) .fontWeight(AppFonts.WEIGHT_BOLD) .fontColor(AppColors.TEXT_WHITE) .backgroundColor(this.getCategoryColor()) .width(15) .height(15) .borderRadius(7.5) .textAlign(TextAlign.Center) }这个结构把“主题”和“序号”放在同一视觉区域。序号是当前分类内的顺序不是全局知识 ID。用户从基础、DNA、病毒切换时序号会重新从 1 开始这和ForEach(this.points, ..., index)的实现一致。十、点击列表项把详情所需字段一次性传过去列表项点击后跳转详情页router.pushUrl({ url: views/learning/KnowledgeDetailPage, params: { id: kp.id, title: kp.title, category: kp.category, summary: kp.summary, content: kp.content } })当前实现直接把详情页需要的字段作为路由参数传递。这样详情页不需要再从全量知识数组里查一次页面打开逻辑简单。代价是路由参数变长。当前内容是本地短文本还能接受如果未来知识正文变成长文章、富文本或图片组合更稳的方式是只传id详情页根据id查询本地模型或仓储服务。当前源码尚未抽服务层文章不把它写成已实现能力。十一、详情页做参数兜底和文本渲染KnowledgeDetailPage读取列表页传入的参数aboutToAppear(): void { const params router.getParams() as KnowledgeDetailParams | undefined if (params?.id) this.kpId params.id if (params?.title) this.title this.normalizeDisplayText(params.title, 知识点) if (params?.category) this.category this.normalizeDisplayText(params.category, 基础) if (params?.summary) this.summary this.normalizeDisplayText(params.summary, this.summary) if (params?.content) this.content this.normalizeDisplayText(params.content, this.content) }默认状态也给了兜底State title: string 知识点 State category: string 基础 State summary: string 通过分类知识点快速理解对应生命科学概念。 State content: string 结合实验现象、可视化反馈和生活实例进行理解可以让知识点更容易记住。这意味着即使路由参数缺失详情页也不会空白。它会显示默认知识点文案。对教学类应用来说这种兜底比直接崩溃更符合用户预期。十二、详情页还根据知识点映射相关实验详情页源码里有getRelatedExperiment()会根据kpId和category选择相关实验private getRelatedExperiment(): RelatedExp { const id this.kpId const cat this.category let resId microscope_observation let resName 显微镜观察洋葱表皮 if (cat 培养) { if (id culture_2 || id culture_4 || id culture_7 || id culture_10) { resId sterile_operation resName 无菌操作挑战 } else { resId bacteria_culture resName 培养细菌菌落 } } return { id: resId, name: resName } }这说明知识点详情不只是静态文本它已经准备了“知识点 - 相关实验”的映射逻辑。需要注意的是文章本文聚焦列表页相关实验入口是否已经完整出现在详情 UI 中需要按详情页后续源码继续复核不能只凭这个函数声称全链路已经打通。十三、安全区和页面滚动处理方式一致知识点列表页读取顶部和底部安全区StorageProp(statusBarHeight) statusBarHeight: number 36 StorageProp(bottomBarHeight) bottomBarHeight: number 0根容器统一避让.width(100%) .height(100%) .backgroundColor(AppColors.PAGE_BG) .padding({ top: this.statusBarHeight, bottom: this.bottomBarHeight })列表区域使用layoutWeight(1)这样顶部导航固定列表内容占剩余空间。知识点数量较多时列表滚动不会把顶部导航挤走也不会让底部内容直接贴住系统栏。十四、当前没有实现搜索、收藏和远程更新从KnowledgeListPage源码看当前知识点列表只做分类过滤和跳转详情。没有搜索框没有收藏状态没有请求接口也没有分页加载。这个边界必须写清楚。能力当前源码是否支持证据分类入口支持LearningPage分类 Grid按分类过滤支持reloadPoints()基础兜底支持无匹配时回到基础图标映射支持getPointIcon()详情跳转支持router.pushUrl()搜索未实现本页无 SearchBar收藏未实现KnowledgePoint无收藏字段远程更新未实现数据来自静态数组学习进度同步未实现本页无 DataStore 写入这张表能帮助读者判断源码真实能力。技术文章不是功能宣传页不能把后续想做的东西写成已经完成。十五、验证清单从分类入口到详情页复核知识点列表时可以按下面顺序步骤操作预期学习中心打开学习页分类网格显示基础、培养、DNA、病毒、遗传、细胞响应式列数改变窗口宽度600、840 宽度阈值后列数变化分类跳转点击 DNA 分类进入列表页并带category: DNA列表过滤查看列表标题和内容显示 DNA 知识点未知分类传入不存在分类列表数据兜底基础分类列表长文本查看摘要标题一行、摘要两行省略详情跳转点击任意知识点打开详情页并带 id/title/category/summary/content返回点击顶部返回调用router.back()如果未来加入搜索或收藏需要额外验证搜索关键字空态、收藏状态持久化、返回后状态恢复等场景。当前源码还不涉及这些能力。十六、常见问题与处理方式问题常见原因处理建议分类入口数量和列表实际数量不一致getKnowledgeCategories()的 count 手写后未同步从getAllKnowledgePoints()统计生成减少手工维护传入未知分类标题异常列表数据兜底但category未重置兜底时同步设置this.category 基础图标映射函数过长所有 ID 判断集中在页面方法抽成映射表或把 icon 放进模型详情参数过长直接把 content 放入路由参数只传 id详情页按 id 查询文本在小屏溢出标题或摘要没限制行数保留maxLines和textOverflow误以为支持搜索项目有通用 SearchBar 组件但本页没使用不在列表页声明搜索能力除非实际接入这些问题都来自源码的真实结构。当前实现已经适合本地静态知识库但如果内容规模继续扩大就需要把分类配置、知识点索引、图标映射和详情查询进一步服务化。十七、小结知识列表页的稳定来自三段式链路细胞工坊的知识点列表链路可以概括为三段学习中心只负责分类入口列表页只负责分类过滤和列表导航详情页负责接收参数并展示内容。KnowledgePoint提供本地静态知识数组KnowledgeCategory提供入口统计和图标颜色KnowledgeListPage用category参数过滤点击后把知识点字段传给详情页。这套实现没有伪装成远程知识库也没有把搜索、收藏、学习进度同步写成已完成能力。它的价值在于清晰、可复核、离线可用。对 HarmonyOS 教学应用来说先把分类入口、过滤兜底、图标映射、长文本保护和详情跳转做稳定再扩展搜索、收藏和进度记录会比一开始堆复杂能力更可靠。
返回列表