【笔下生辉|01】HarmonyOS ArkTS 写作素材首页实战:让分类、推荐与学习进度一眼可扫

发布时间:2026/10/27 9:58:30
【笔下生辉|01】HarmonyOS ArkTS 写作素材首页实战:让分类、推荐与学习进度一眼可扫 写作训练类应用的首页最容易写散上面放一句口号中间放几个按钮下面再堆一串题库卡片看起来信息很多用户真正打开时却不知道先点哪里。笔下生辉的首页实现给了一个比较清晰的工程样本它没有把首页做成纯展示页而是把“今日概览、快捷行动、题型分类、推荐题库、学习进度”按扫读顺序组织起来再通过Index.ets的底部导航和侧边导航适配手机、平板和 2in1。本文只基于本地源码D:\huawei\one17-11可复核内容展开核心文件包括entry/src/main/ets/views/HomePage.ets、entry/src/main/ets/pages/Index.ets、entry/src/main/ets/common/components/BankCard.ets、entry/src/main/ets/common/components/SectionHeader.ets、entry/src/main/ets/mock/MockBanks.ets与entry/src/main/module.json5。文章不记录 AppGallery 后台状态不声称公开发布结果也不虚构源码里不存在的在线推荐、云同步或 AI 写作能力。本文解决四个具体问题首页如何把“能做什么”放到用户第一眼能理解的位置。分类、搜索、题库和错题入口如何从同一页自然分流。学习进度如何从StorageLink与题库卡片联动而不是写死展示数字。手机、平板、2in1 下首页和导航如何避免互相挤占。一、先确认首页不是孤立页面而是 Tab 架构里的首屏HomePage.ets并不是应用入口本身它被pages/Index.ets装进 Tab 容器。这个边界很关键首页只负责首屏信息组织和跳转意图底部安全区、顶部避让、底部导航、侧边导航属于Index。// entry/src/main/ets/pages/Index.ets Builder PageContent() { if (this.currentIndex 0) { HomePage() } else if (this.currentIndex 1) { BankListPage() } else if (this.currentIndex 2) { ExamTab() } else if (this.currentIndex 3) { FavoritePage() } else { MinePage() } }这段代码说明首页只是第一个 Tab。首页里点击“更多”时可以把currentTabIndex改到题库页点击错题复习时可以把favoriteTabIndex与currentTabIndex组合起来。这比在首页里直接复制题库页或错题页要稳因为页面职责不会互相吞掉。对 HarmonyOS 应用来说这个拆分还能减少发布前复查难度如果首页出现滚动、底栏遮挡、侧栏适配问题优先看Index.ets如果卡片进度或分类入口不对再看HomePage.ets和子组件。二、用 StorageLink 建立首页与全局学习状态的连接首页真正要展示的不是静态素材而是用户当前学习状态。源码中HomePage使用多组StorageLink读取全局状态当前 Tab、收藏 Tab、题库进度、考试历史、收藏记录、错题记录、当前断点。StorageLink(currentTabIndex) currentTabIndex: number 0 StorageLink(favoriteTabIndex) favoriteTabIndex: number 0 StorageLink(bankProgress) progressList: BankProgress[] [] StorageLink(examHistory) examHistory: ExamHistory[] [] StorageLink(favoriteRecords) favRecords: FavoriteRecord[] [] StorageLink(wrongRecords) wrongRecords: WrongRecord[] [] StorageLink(currentBreakpoint) currentBp: string sm State pageWidth: number 360这些状态各自有明确用途状态首页用途失效后的表现currentTabIndex首页按钮切换到题库、挑战、错题等 Tab点击后仍停留首页favoriteTabIndex错题入口指定收藏页内部 Tab进入错题页后默认分区不对bankProgress推荐题库卡片显示已答数量和进度题库卡片看起来像静态列表examHistoryHero 统计挑战次数今日概览缺少反馈favoriteRecordsHero 统计收藏数收藏行为没有被首页感知wrongRecords快捷行动和底栏徽标错题复习入口缺少紧迫感currentBreakpoint决定导航和题库布局宽屏仍使用窄屏布局首页通过StatService.summarize(...)把这些状态归并成展示指标这个选择比直接在 UI 中到处写统计表达式更利于维护。private myStats() { return StatService.summarize( this.progressList, this.examHistory, this.favRecords, this.wrongRecords ) }工程上要注意一点myStats()在Hero()中会被多次调用如果后续统计逻辑变重可以把统计结果提升为 ViewModel 或在状态变化时缓存。当前源码里统计来源是本地数组首页规模也不大这种写法仍可接受。三、Hero 区先回答“今天有什么可做”首页 Hero 区不是简单 banner。它把总题量、题型覆盖、已答、正确率、挑战次数、收藏数放在一块让用户打开应用后能立即判断自己处在什么状态。Builder Hero() { Column() { Column({ space: 14 }) { Row() { Column({ space: 6 }) { Text(今日挑战) Text(${TOTAL_QUESTIONS.toLocaleString()} 题) Text(覆盖 ${TOTAL_REGIONS} 类语文纠错) } Blank() Image($r(app.media.img_home_china_map)) } Row({ space: 8 }) { this.HeroStat(${this.myStats().totalAnswered}, 已答) this.HeroStat(${this.myStats().accuracyPercent}%, 正确率) this.HeroStat(${this.myStats().examCount}, 挑战) this.HeroStat(${this.myStats().favoriteCount}, 收藏) } } .linearGradient({ angle: 135, colors: [[Colors.PRIMARY, 0], [Colors.PRIMARY_DARK, 1]] }) } }这里有两个可复用点第一TOTAL_QUESTIONS与TOTAL_REGIONS来自MockBanks.ets不是在页面里硬编码。首页只消费“目录层统计”数据源负责维护题库规模。第二HeroStat把统计块封装成小 Builder避免四个统计项在布局、背景、字体上逐个复制。Builder HeroStat(value: string, label: string) { Column({ space: 3 }) { Text(value) .fontSize(Sizes.TITLE_FONT) .fontWeight(FontWeight.Bold) .fontColor(Color.White) Text(label) .fontSize(Sizes.SMALL_FONT) .fontColor(#E6FFFFFF) } .layoutWeight(1) .height(50) .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) .borderRadius(14) .backgroundColor(#33000000) }如果要继续优化可以减少#E6FFFFFF、#33000000这类局部硬编码把它们沉到主题常量或资源色里。源码已经有Colors和Sizes首页后续最好继续沿用这条路径。四、快捷行动不是按钮堆叠而是首页任务分流QuickActions()把最重要的开始挑战放成主行动卡限时挑战和错题复习放成次级行动。这种结构适合训练型应用用户首次进入先看到主路径老用户回来能直接处理错题或限时练习。Builder QuickActions() { Column({ space: 10 }) { this.PrimaryActionCard() Row({ space: 10 }) { this.ActionCard(限时挑战, 60 秒快速纠错, () { this.currentTabIndex 2 }) this.ActionCard(错题复习, ${this.wrongRecords.length} 道错题, () { this.favoriteTabIndex 2 this.currentTabIndex 3 }) } } }这段源码体现了两个设计判断行动跳转方式为什么这样写开始挑战currentTabIndex 1进入题库/训练主路径限时挑战currentTabIndex 2切到挑战 Tab降低路径成本错题复习favoriteTabIndex 2currentTabIndex 3先定位收藏页内部错题分区再切外层 TabActionCard对长文案也做了基本防护标题和副标题都设置maxLines(1)与textOverflow(Ellipsis)。首页这种卡片在中文环境里很容易遇到“错题复习 123 道错题”长度变化提前加约束能减少适配时的截断和挤压。五、题型分类用 Flex 包裹适合首页短入口分类区不是完整分类页只展示前 7 个分类加“全部”。源码用Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween })做短入口宫格。Builder CategoryStrip() { Column() { Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) { ForEach(CATEGORIES.slice(0, 7), (cat: Category) { this.CategoryItem(cat) }, (cat: Category) cat.type) this.CategoryAllItem() } } .padding({ left: 10, right: 10, top: 12, bottom: 4 }) .margin({ left: Sizes.PADDING_LARGE, right: Sizes.PADDING_LARGE }) .backgroundColor(#EAFBF8F0) .borderRadius(10) }每个分类项点击后进入搜索页并把分类类型与分类名称作为路由参数传递。router.pushUrl({ url: pages/SearchPage, params: { categoryType: cat.type, categoryName: cat.name } })这个实现适合“写作素材首页”的首屏因为用户并不需要在首页展开全部题库树。首页只负责让用户快速选择题型方向例如错别字、病句、标点、词语误用、网络热词、古诗纠错等详细筛选留给SearchPage或分类页。需要注意的是源码当前CategoryItem宽度固定为30.5%高度76。这在手机三列入口里比较稳定但宽屏下分类区仍是短入口而非真正自适应分类看板。如果后续要把分类区做成平板强化模块可以考虑让宽屏使用 4 列或 6 列而不是简单放大三列卡片。六、推荐题库卡片承载进度而不是只展示题库名称推荐题库区调用BankCard卡片内部会根据bankProgress计算真实完成数、正确率和进度比例。private realAccuracy(): number { const p UserDataManager.getProgress(this.progressList, this.bank.id) if (p p.finished 0) return p.correct / p.finished return this.bank.accuracy } private realFinished(): number { const p UserDataManager.getProgress(this.progressList, this.bank.id) return p ? p.finished : 0 } private progressRatio(): number { if (this.bank.totalCount 0) return 0 return Math.min(this.realFinished() / this.bank.totalCount, 1) }这三段方法分别负责准确率、已完成数量和进度百分比。它们的好处是页面不关心进度数据如何查找只负责把结果显示出来UserDataManager.getProgress(...)的数据查找逻辑也不会散落到多个 Builder 里。卡片正文还处理了长标题和小宽度场景Text(this.bank.name) .layoutWeight(1) .constraintSize({ minWidth: 0 }) .fontSize(Sizes.TITLE_FONT) .fontWeight(FontWeight.Bold) .fontColor(Colors.TEXT_PRIMARY) .maxLines(1) .textOverflow({ overflow: TextOverflow.Ellipsis })constraintSize({ minWidth: 0 })与layoutWeight(1)配合使用能减少 Row 内文字把右侧按钮挤出的问题。首页卡片里同时存在封面图、题库名称、标签、进度条和“进入”按钮这类约束比单纯写width(100%)更可靠。七、推荐题库在宽屏下切换为网格首页是否使用网格不只看断点也看实际页面宽度。private useGridLayout(): boolean { return this.currentBp lg this.pageWidth 700 } .onAreaChange((oldArea: Area, newArea: Area) { const width Number(newArea.width) if (width 0) this.pageWidth width })然后BankSection()按条件切换布局Builder BankSection() { if (this.useGridLayout()) { Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) { ForEach(BANKS, (bank: Bank) { Column() { BankCard({ bank: bank }) } .width(32%) .margin({ bottom: 12 }) }, (bank: Bank) bank.id) } } else { Column({ space: 12 }) { ForEach(BANKS, (bank: Bank) { BankCard({ bank: bank }) }, (bank: Bank) bank.id) } } }这比只用currentBreakpoint更稳。原因是折叠屏、小窗、2in1 可调整窗口下同一个断点名不一定完全等于内容区可用宽度。源码通过pageWidth 700再确认一次避免内容区窄却强行塞三列。BankCard自身还会用cardWidth 280切换CompactCard()形成两级保护层级判断点保护目标HomePage页面宽度是否足够决定题库列表还是三列网格BankCard卡片自身宽度是否足够决定横向卡片还是紧凑卡片这种组合很适合 HarmonyOS 多设备布局。页面控制区域密度组件控制自身可读性。八、Index 负责安全区与导航形态首页底部留了Blank().height(92)但真正的底部安全区和导航高度由Index.ets处理。private bottomSafePadding(): number { return Math.max( Sizes.BOTTOM_NAV_MIN_PADDING, this.getUIContext().px2vp(this.navigationIndicatorHeightPx) ) } private topSafePadding(): number { return Math.max( Sizes.PADDING_SMALL, this.getUIContext().px2vp(this.topAvoidAreaHeightPx) ) } private bottomNavHeight(): number { return Sizes.TAB_BAR_HEIGHT this.bottomSafePadding() }底部导航区域使用Sizes.TAB_BAR_HEIGHT bottomSafePadding()页面根容器再加顶部避让。这说明首页没有直接操控系统窗口只是在 Tab 内容区滚动。对 AppGallery 适配复查来说这个边界很重要如果底部按钮被系统手势条挡住先检查navigationIndicatorHeightPx的注入和bottomSafePadding()如果首页内容最后一张卡片被底栏遮挡再检查Blank().height(92)是否和真实底栏高度匹配。源码还根据currentBp选择底部导航或侧边导航if (this.currentBp sm || this.currentBp md || this.currentBp lg) { // 底部导航 Column() { Stack() { this.PageContent() } Column() { Row() { this.BottomNavItem(0) this.BottomNavItem(1) this.BottomNavItem(2) this.BottomNavItem(3) this.BottomNavItem(4) } } } } else { // 侧边导航 内容区 Row() { Column({ space: 8 }) { this.SideNavItem(0) this.SideNavItem(1) this.SideNavItem(2) this.SideNavItem(3) this.SideNavItem(4) } Stack() { this.PageContent() } } }当前判断把sm/md/lg都归到底部导航其他断点才走侧边导航。文章不能推断它已经覆盖所有平板策略只能说源码预留了底部/侧边两种导航形态实际断点命名与注入逻辑还需要结合项目的断点服务复核。九、数据目录来自 MockBanks而不是页面临时拼装MockBanks.ets是首页题库信息的主要来源。它定义了BANK_SPECS、REGIONS、CATEGORIES、BANKS并通过syncCatalogCounts()同步题库数量和分类数量。export const REGIONS: Region[] BANK_SPECS.map((spec: BankSpec) { return { id: spec.regionId, name: spec.name, shortName: spec.shortName, cover: spec.cover } as Region }) export const CATEGORIES: Category[] [ { type: typo, name: 错别字, count: 0 }, { type: sentence, name: 病句, count: 0 }, { type: punctuation, name: 标点, count: 0 }, { type: usage, name: 词语误用, count: 0 }, { type: internet, name: 网络热词, count: 0 }, { type: poetry, name: 古诗词, count: 0 } ]源码中终端读取到的部分中文因控制台编码显示为乱码但结构可以复核分类类型包括typo、sentence、punctuation、usage、internet、poetry题库卡片使用bank.id、bank.cover、bank.totalCount、bank.hot和章节数据。TOTAL_QUESTIONS和TOTAL_REGIONS也从同一目录数据计算出来syncCatalogCounts() export const TOTAL_QUESTIONS: number BANKS.reduce((sum: number, bank: Bank) sum bank.totalCount, 0) export const TOTAL_REGIONS: number REGIONS.length这让首页 Hero 的数字和题库列表保持同源。后续如果题库扩容只要目录同步逻辑正确首页总数和卡片数量不会互相打架。十、主题常量已经覆盖主要视觉语义首页引用Colors与Sizes主题常量集中在ThemeConstants.ets。源码里可以看到主色、背景、文本、分割线、状态色、字体尺寸、卡片圆角、底栏高度等定义。export class Colors { static readonly PRIMARY: string #2F6B5E static readonly PRIMARY_DARK: string #1F5246 static readonly PRIMARY_LIGHT: string #E6EFEB static readonly ACCENT: string #8A6814 static readonly BACKGROUND: string #F5F1E8 static readonly SURFACE: string #FFFFFF static readonly TEXT_PRIMARY: string #1F2A26 static readonly TEXT_SECONDARY: string #444444 static readonly TEXT_HINT: string #5F6763 static readonly ERROR: string #B3261E } export class Sizes { static readonly H2_FONT: number 18 static readonly TITLE_FONT: number 16 static readonly BODY_FONT: number 14 static readonly CAPTION_FONT: number 12 static readonly SMALL_FONT: number 10 static readonly TAB_BAR_HEIGHT: number 60 static readonly BOTTOM_NAV_MIN_PADDING: number 28 }这对首页很重要。写作训练类应用的页面容易同时出现标题、说明、标签、进度、按钮、徽标如果每个组件自己挑颜色后期调深浅色或对比度会非常困难。当前源码至少把大部分核心语义收口到了Colors和Sizes。不过源码也存在局部硬编码例如分类容器的#EAFBF8F0、卡片背景#FFFDF7、边框#E2D4B4以及 Hero 统计块的半透明色。发布前如果要做更严格的深浅色复查应把这些局部色也纳入主题表并确认文本对比度。十一、发布复查时要检查源码能支撑哪些宣传语entry/src/main/module.json5声明设备类型为phone、tablet、2in1{ module: { name: entry, type: entry, deviceTypes: [ phone, tablet, 2in1 ], pages: $profile:main_pages, abilities: [ { name: EntryAbility, srcEntry: ./ets/entryability/EntryAbility.ets, icon: $media:layered_image, label: $string:EntryAbility_label, startWindowIcon: $media:layered_image, startWindowBackground: $color:start_window_background, exported: true } ] } }因此本文可以讨论多设备首页布局和导航适配但不能把它说成已经完成全部设备实机验收。源码提供的是布局实现依据真正的上架材料仍要用设备截图、安装启动、旋转/缩放、小窗、深浅色等测试结果来闭环。同样首页题库数据来自本地MockBanks.ets文章可以说它是本地题库目录和学习进度首页不能说它有云端推荐、在线模型生成素材、跨端同步学习记录。保持宣传语和源码一致才是 AppGallery 复查时最省成本的做法。十二、可复核清单与常见问题发布前可以按下面清单复查首页检查项证据文件通过标准首页是否在首个 Tabpages/Index.etscurrentIndex 0渲染HomePage()底部导航是否避让系统手势区pages/Index.etsbottomSafePadding()使用系统导航高度和最小值首页是否可滚动views/HomePage.ets根容器为Scroll()末尾留底部空白题型分类是否可跳转views/HomePage.etsCategoryItem跳转SearchPage并传参推荐题库是否展示真实进度BankCard.ets通过UserDataManager.getProgress读取进度宽屏是否改变题库布局views/HomePage.etscurrentBp lg pageWidth 700使用三列卡片窄宽是否降级BankCard.etscardWidth 280使用紧凑布局主题是否集中ThemeConstants.ets主要颜色和尺寸来自Colors、Sizes常见问题也可以直接按源码边界定位问题先看哪里修复方向首页最后一张题库卡被底栏挡住Index.bottomNavHeight()、HomePage末尾Blank确认系统导航高度注入必要时调整内容底部留白平板上题库卡太窄HomePage.useGridLayout()、BankCard.useCompactLayout()同时检查页面宽度阈值和卡片自身宽度阈值错题入口数量不更新wrongRecords的StorageLink检查错题写入和全局状态同步分类点击后没有筛选效果CategoryItem路由参数、SearchPage接收逻辑确认categoryType与categoryName被读取首页统计和题库列表数字不一致MockBanks.syncCatalogCounts()确认题库扩容后同步目录数量文案在按钮中截断ActionCard、BankCard文本约束继续使用maxLines、textOverflow、constraintSize总结笔下生辉首页的工程价值在于它把写作训练入口做成了一个可扫读的任务面板而不是堆满入口的展示页。Index管导航和安全区HomePage管首屏信息架构MockBanks管本地题库目录BankCard管题库进度和详情跳转主题常量管主要视觉语义。这个边界清楚以后首页既能服务手机用户快速开始也能在平板和 2in1 上通过网格与侧边导航保留扩展空间。本文的唯一工程标记是com.jiaweikang.one17。如果后续进入公开发布阶段应继续以平台真实草稿、公开页和发布时间为准不用草稿结果替代公开发布结果。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询