鸿蒙原生实战:用 ArkUI 搭 ACG 社区首页 —— 沉浸式头图与帖子信息流

应用背景:12 ACG 社区(acg-community)是鸿蒙原生校园工具系列的第 12 个应用,定位"校园二次元爱好者社区",采用玫红主题 #DB2777、强调色 #F472B6。本文逐行拆解 HomeTab.ets(共 312 行),聚焦 沉浸式全屏安全区适配HeroCard 渐变头图WeekChart 发帖趋势条形图PostList 帖子信息流 四大核心模块,并穿插 SearchBarQuickActionRankList 等辅助组件。

与第 11 号"校园快递"不同,12 号应用是内容社区:它的首页核心不是"任务提醒",而是"内容消费"。用户打开首页,第一眼要看到的是"社区在聊什么、有什么新帖、哪个板块最火"。因此首页的信息密度与情感色彩都更浓——玫红渐变头图奠定"二次元"的年轻气质,帖子列表用点赞数与星级制造"热度感",板块排行则引导用户进入细分话题。理解首页,就是理解"内容社区如何用一屏留住用户"。

一、页面骨架与沉浸式安全区

首页根结构依旧是本系列统一的 Header + Scroll 二分式,ScrolllayoutWeight(1) 撑满剩余空间:

build() {
  Column() {
    this.Header()
    Scroll() {
      Column({ space: 14 }) {
        this.HeroCard()
        this.SearchBar()
        this.QuickAction()
        this.WeekChart()
        this.SectionTitle('热门帖子', '全部 >')
        this.PostList()
        this.SectionTitle('板块排行', '详情 >')
        this.RankList()
      }
      .width('100%')
      .padding({ left: D.pad, right: D.pad, top: 14, bottom: D.pad + this.safeBottom + 20 })
    }
    .layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top)
  }
  .width('100%').height('100%').backgroundColor(C.bg)
}

@StorageProp('safeTop') / @StorageProp('safeBottom')AppStorage 读取避让区,值由 EntryAbility.onWindowStageCreate() 通过 setWindowLayoutFullScreen(true) + getWindowAvoidArea() 计算写入。底部 paddingD.pad + this.safeBottom + 20 表达式与前作一致,保证最后一张卡片不被底部导航条遮挡。

值得一提的是,本应用的沉浸式实现里,setWindowSystemBarProperties 把状态栏文字色设成了深色 #1C2333(与 C.text 一致),而首页 Header 背景是白卡、HeroCard 是玫红渐变——这意味着状态栏文字在白色标题栏上呈深色、在渐变卡上方则因 safeTop 的补偿而让出空间。深浅两种底色都能兼容,靠的正是"状态栏文字色统一深色 + 各页面自行用 safeTop 避让"这条约定。

项目源码开源:https://gitee.com/codenestFlow/HarmonyOSHub

配图

二、数据模型:五类结构

首页把零散字段收敛为 5 个接口,其中 Post 最能体现"内容社区"的数据特点:

interface Post {
  id: number; emoji: string; title: string; author: string;
  time: string; tag: string; rating: number; progress: number; likes: string;
}
interface StatItem { value: string; label: string; color: string; }
interface WeekBar { day: string; value: number; }
interface RankItem {
  id: number; rank: number; name: string; value: string; emoji: string; percent: number;
}
interface QuickItem { emoji: string; label: string; }

对比 11 号快递的 ExpressPostcode(单号)换成了 likes(点赞数)、把 company 换成了 author(作者)——这就是"物流"与"社区"的数据差异:物流关心"谁送、单号多少",社区关心"谁写、多少人赞"。rating(星级)在两个应用里都保留了,因为"评分"是通用的质量信号。tag 在这里是"板块标签"(追番/游戏/创作),而非快递的"状态标签"。

RankItemvalue 从"8 件"变成了"452帖"——社区排行的单位是"帖子数"。这些细微的字段语义差异,正是"同一套骨架、不同业务数据"的生动例证。当你用统一模板开发系列应用时,接口的字段命名要随业务调整,但"先抽象接口、再渲染 UI"的范式始终不变。

三、Header 与 HeroCard 渐变头图

Header 与 11 号一字不差(标题「ACG社区」),重点在 HeroCard——它用 linearGradient 铺出玫红 → 浅粉的渐变,承载社区名、三栏统计与双按钮:

// HeroCard — 渐变头图
@Builder HeroCard() {
  Column({ space: 16 }) {
    Row() {
      Text('次元壁 · ACG爱好者的家').fontSize(13).fontColor('#FFFFFF').opacity(0.85)
      Blank()
      Text('今日热榜').fontSize(11).fontColor('#FFFFFF').opacity(0.7)
    }.width('100%')

    Row({ space: 12 }) {
      ForEach(this.stats, (s: StatItem) => {
        Column({ space: 4 }) {
          Text(s.value).fontSize(22).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
          Text(s.label).fontSize(11).fontColor('#FFFFFF').opacity(0.8)
        }.layoutWeight(1).alignItems(HorizontalAlign.Center)
      }, (s: StatItem) => s.label)
    }.width('100%')

    Row({ space: 12 }) {
      Button('发布帖子')
        .fontSize(14).fontColor(C.primary).backgroundColor('#FFFFFF')
        .borderRadius(D.rSm).height(40).layoutWeight(1)
        .onClick(() => { promptAction.showToast({ message: '发布帖子' }); })
      Button('加入圈子')
        .fontSize(14).fontColor('#FFFFFF').backgroundColor('#33FFFFFF')
        .borderRadius(D.rSm).height(40).layoutWeight(1)
        .onClick(() => { promptAction.showToast({ message: '加入圈子' }); })
    }.width('100%')
  }
  .width('100%').padding(20).borderRadius(D.rLg)
  .linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] })
}

渐变从 #DB2777(玫红)过渡到 #F472B6(浅粉),135° 角让高光落在右上。三栏统计是"今日新帖 128 / 活跃用户 2.4k / 本周话题 892"——注意 2.4k 这种缩写数值,社区类应用常用 k 表示千,避免数字过长挤压布局。双按钮"发布帖子"(白底玫红字,主操作)与"加入圈子"(半透明白底白字,次操作),主次分明的视觉层级与 11 号一脉相承。

一个值得点出的细节:社区的头图 Slogan「次元壁 · ACG爱好者的家」比快递的「菜鸟驿站 · 梅园站点」更具情感色彩。头图文案从"地理坐标"变成"情感归属",这是内容社区区别于工具类应用的重要信号——社区卖的是"认同感",头图就是这种认同感的第一块招牌。

ACG社区首页顶部 · 玫红渐变头图与快捷入口

四、SearchBar 与 QuickAction 快捷入口

SearchBar 结构与前作一致,但占位文案变成了「搜索帖子、话题、用户」——三对象精确覆盖社区的搜索场景:

// SearchBar — 搜索
@Builder SearchBar() {
  Row({ space: 10 }) {
    Text('🔍').fontSize(18)
    TextInput({ placeholder: '搜索帖子、话题、用户', text: this.searchText })
      .backgroundColor('transparent').borderRadius(D.rSm).height(40)
      .layoutWeight(1).placeholderColor(C.textDim).placeholderFont({ size: 14 })
      .onChange((val: string) => { this.searchText = val; })
  }
  .width('100%').padding({ left: 14, right: 14 })
  .backgroundColor(C.card).borderRadius(D.rMd)
  .border({ width: 1, color: C.stroke })
}

QuickAction 四宫格从"寄件/取件/代取/查件"换成"✏️发帖 / 🔥热门 / 🏆排行 / 💬消息":

// QuickAction — 快捷入口
@Builder QuickAction() {
  Row({ space: 10 }) {
    ForEach(this.quickActions, (item: QuickItem) => {
      Column({ space: 6 }) {
        Text(item.emoji).fontSize(26)
        Text(item.label).fontSize(12).fontColor(C.textSub)
      }
      .layoutWeight(1).padding({ top: 14, bottom: 14 })
      .backgroundColor(C.card).borderRadius(D.rMd)
      .border({ width: 1, color: C.stroke })
      .onClick(() => { promptAction.showToast({ message: item.label }); })
    }, (item: QuickItem) => item.label)
  }.width('100%')
}

四个入口里"发帖"是内容生产的起点、"热门/排行"是内容发现、"消息"是社交互动——恰好构成社区用户动线的完整闭环(生产 → 发现 → 互动)。这个四宫格虽然布局简单,但每一项都对应社区的顶层功能,是"入口即导航"的典型设计。

五、WeekChart 本周发帖趋势

条形图与前作共享同一套数学公式,但数据语义变成了"发帖量":

// WeekChart — 发帖趋势
@Builder WeekChart() {
  Column({ space: 12 }) {
    Row() {
      Text('本周发帖趋势').fontSize(15).fontWeight(FontWeight.Bold).fontColor(C.text)
      Blank()
      Text('峰值 95 帖').fontSize(12).fontColor(C.textDim)
    }.width('100%')

    Row({ space: 6 }) {
      ForEach(this.weekBars, (w: WeekBar) => {
        Column({ space: 6 }) {
          Column() {
            Column()
              .width('100%')
              .height(Math.floor(w.value / this.maxBar * 80))
              .backgroundColor(C.primary).borderRadius(4)
          }.width('100%').height(80)
          Text(w.day).fontSize(11).fontColor(C.textDim)
        }
        .layoutWeight(1).alignItems(HorizontalAlign.Center)
      }, (w: WeekBar) => w.day)
    }.width('100%')
  }
  .width('100%').padding(14)
  .backgroundColor(C.card).borderRadius(D.rLg)
  .border({ width: 1, color: C.stroke })
}

公式 Math.floor(w.value / this.maxBar * 80) 与前作一字不差——这正是"系列化 demo"的价值:一处成熟的图表写法被跨应用复用,读者只需换 weekBars 数据和标题。本页数据 [65, 88, 52, 95, 78, 90, 72] 是一周发帖量的模拟分布,maxBar = 95 对应"峰值 95 帖"。

社区语境下,这组"发帖趋势"其实承载了"社区活跃度"的信号——用户看到周六峰值、周日回落,能直观感知社区的活跃节奏。把抽象的发帖量画成柱子,比摆一个"本周共 540 帖"的数字更有说服力,这是数据可视化的核心价值:让趋势可见

ACG社区首页中部 · 发帖趋势与热门帖子

六、PostList 帖子信息流(核心)

这是首页的灵魂。每条帖子卡片包含 emoji、标题、板块标签、作者·时间、星级、点赞数与进度条:

// PostList — 帖子列表
@Builder PostList() {
  Column({ space: 10 }) {
    ForEach(this.posts, (p: Post) => {
      Column({ space: 10 }) {
        Row({ space: 12 }) {
          Row() { Text(p.emoji).fontSize(22) }
            .width(46).height(46)
            .backgroundColor(C.primarySoft).borderRadius(D.rSm)
            .justifyContent(FlexAlign.Center)

          Column({ space: 4 }) {
            Row({ space: 8 }) {
              Text(p.title).fontSize(14).fontWeight(FontWeight.Medium).fontColor(C.text)
              Text(p.tag).fontSize(10).fontColor(C.primary)
                .padding({ left: 6, right: 6, top: 2, bottom: 2 })
                .backgroundColor(C.primarySoft).borderRadius(4)
            }.width('100%')
            Text(p.author + ' · ' + p.time).fontSize(11).fontColor(C.textDim)
            Row({ space: 2 }) {
              ForEach([1, 2, 3, 4, 5], (n: number) => {
                Text(n <= p.rating ? '★' : '☆').fontSize(12).fontColor(n <= p.rating ? C.warn : C.textDim)
              }, (n: number) => n.toString())
            }.width('100%')
          }
          .alignItems(HorizontalAlign.Start).layoutWeight(1)

          Column({ space: 4 }) {
            Text('❤ ' + p.likes).fontSize(13).fontColor(C.danger).fontWeight(FontWeight.Bold)
            Text(p.progress === 100 ? '精华' : '热门').fontSize(10).fontColor(C.textDim)
          }
          .alignItems(HorizontalAlign.End)
        }
        .width('100%')

        Progress({ value: p.progress, total: 100 }).color(C.primary).width('100%')
      }
      .width('100%').padding(14)
      .backgroundColor(C.card).borderRadius(D.rMd)
      .border({ width: 1, color: C.stroke })
      .onClick(() => { promptAction.showToast({ message: p.title }); })
    }, (p: Post) => p.id.toString())
  }.width('100%')
}

与 11 号快递的 ExpressList 相比,PostList 有三处关键差异,恰好体现"社区"与"物流"的业务分野:

其一,板块标签用主色而非状态色。快递的状态标签是 tag === '待取件' ? C.warn : C.ok(警示/成功二态);社区的板块标签则是固定的 fontColor(C.primary) + backgroundColor(C.primarySoft)(玫红 + 浅粉底)。原因很直白:板块标签是"分类"(追番/游戏/创作),没有好坏之分,不该用黄绿去暗示状态,用品牌色统一标注即可。这提醒我们——颜色语义要匹配字段语义,分类用品牌色、状态用警示色、成功用绿色,别混用。

其二,点赞数用 ❤ + likes 并以 C.danger(红)高亮。这是社区特有的"热度信号":红心 + 数字是全网通用的"赞"符号,用户扫一眼就能感知帖子热度。而 p.progress === 100 ? '精华' : '热门' 把"进度条拉满"映射成"精华帖"标签,进度条在这里不再是"运输进度",而是"内容质量分/热度分"的可视化——同一组件,语义因业务而彻底改变。

其三,作者信息 p.author + ' · ' + p.time 用点号拼接。社区要突出"谁在何时写了什么",作者名与时间并排呈现,是内容平台的标配信息架构。快递则只需"时间 + 状态",不需要作者维度。

这三点差异浓缩成一个结论:UI 骨架可以复用,但字段语义、颜色语义、拼接方式必须随业务重写。系列化开发的精髓,正是在"复用"与"重写"之间找到平衡。

七、RankList 板块排行

排行榜把社区各板块的帖子量做了排序展示,引导用户进入热门话题:

// RankList — 板块排行
@Builder RankList() {
  Column({ space: 10 }) {
    ForEach(this.ranks, (r: RankItem) => {
      Row({ space: 12 }) {
        Text(r.rank.toString()).fontSize(16).fontWeight(FontWeight.Bold)
          .fontColor(r.rank === 1 ? C.warn : C.textDim).width(24)
        Text(r.emoji).fontSize(20)
        Text(r.name).fontSize(14).fontColor(C.text).layoutWeight(1)
        Text(r.value).fontSize(14).fontColor(C.primary).fontWeight(FontWeight.Medium)
      }
      .width('100%').padding({ left: 14, right: 14, top: 10, bottom: 10 })
      .backgroundColor(C.card).borderRadius(D.rSm)
      .border({ width: 1, color: C.stroke })
      .onClick(() => { promptAction.showToast({ message: r.name }); })
    }, (r: RankItem) => r.id.toString())
  }.width('100%')
}

第一名「追番讨论 452帖」用 C.warn 橙色高亮序号,其余淡化。数据"追番讨论 452 / 游戏攻略 318 / 同人创作 276"三个板块,与 HeroCard 的"今日新帖 128"、WeekChart 的周趋势形成数据闭环——首页的信息架构是"总览(头图)→ 趋势(图表)→ 内容(帖子)→ 分类(排行)",层层递进,符合用户"先看大盘、再看细节、最后分流"的浏览习惯。

ACG社区首页底部 · 完整信息流收尾

八、与快递首页的"同源异构"

把 12 号 ACG 社区与 11 号校园快递的首页并排看,是理解"系列化开发"的最佳样本:两者的 HeaderSearchBarQuickActionWeekChartSectionTitleRankList 几乎逐字相同(同源),但 HeroCard 的文案、PostList/ExpressList 的字段语义与颜色语义完全重写(异构)。

这种"同源异构"正是本系列的价值主张:骨架沉淀为模板,业务重写为差异。11 号教你"物流信息流怎么写",12 号教你"内容信息流怎么写",两者共享的骨架让你快速上手,两者的差异让你理解"业务如何塑造 UI"。学会抽象出"头图 + 图表 + 列表 + 排行"这套社区信息流模板,你就能用半天时间搭出任意一个内容社区 App 的首页。

九、本章小结与工程经验

模块核心技术学习价值
Header@StorageProp safeTop沉浸式安全区适配
HeroCard玫红渐变 + 双按钮内容社区头图
QuickAction四宫格导航生产/发现/互动闭环
WeekChartMath.floor 映射高度发帖趋势可视化
PostList❤ 点赞 + 板块标签 + 精华态内容信息流标准写法
RankList条件着色排名板块排行引导

首页最值得迁移的是"内容信息流"范式:一张情感化渐变头图(Slogan + 三栏统计 + 双按钮)奠定社区气质,一张发帖趋势图让活跃度可见,一条帖子列表承载内容消费(标题 + 板块标签 + 作者时间 + 星级 + 点赞 + 热度进度),一张板块排行引导用户分流。四块拼起来,就是内容社区首页的完整骨架。

其次要记住颜色语义匹配字段语义这条铁律:分类标签用品牌色(C.primary)、状态标签用警示/成功色(C.warn/C.ok)、点赞等情感信号用红色(C.danger)。颜色不是随意取的装饰,它是"无声的文案",用错了会让用户误读信息。

ACG社区首页下部 · 板块排行

十、帖子数据与交互的深层解读

首页四条帖子数据,本身就是 ACG 社区内容生态的微型样本:🌸 2025夏番追番清单分享(追番)、🎮 原神5.0版本攻略合集(游戏)、🎨 同人画作每周精选Vol.42(创作)、📚 轻小说推荐:异世界题材(轻小说)——四条帖子恰好覆盖 ACG 四大内容板块,与 HeroCard 的"本周话题 892"、RankList 的"追番讨论/游戏攻略/同人创作"形成完整闭环。这种"数据互相呼应"的编排,让首页显得真实可信,而不是一堆随机 mock。

星级评分用 ForEach([1, 2, 3, 4, 5]) 生成五颗星,n <= p.rating ? '★' : '☆' 决定实心或空心,fontColor(n <= p.rating ? C.warn : C.textDim) 让实心星呈琥珀色、空心星呈灰色。这是"固定数组造循环"的经典用法,五颗星不重复、key 用 n.toString() 稳定标识。若未来要支持半星,可把 rating 从整数改为 0.5 步进的浮点,再把判断改成"满星/半星/空星"三分支——但 demo 用整数星级已足够。

❤ 328 这类点赞数用红色 C.danger + 心形 emoji,是全网通用的"热度"视觉符号。用户扫一眼右侧红色数字,就能感知热度排序——506 赞的同人画作显然比 128 赞的轻小说更热。点赞数、星级、精华/热门 标签三者共同构成帖子的"质量信号",引导用户优先点击高热度内容。

可访问性上,帖子卡片做到了"颜色 + 文字双通道":板块标签除了玫红,还有"追番/游戏"文字;精华/热门除了进度条长度,还有文字标签。色弱用户不靠颜色也能读懂信息——这是"靠颜色区分信息时,务必配文字兜底"的无障碍原则。

性能上,ForEach(this.posts, ...) 的 key 用 p.id.toString(),id 唯一稳定,增删帖子时节点能正确复用、不闪烁。未来做"下拉刷新 + 上拉加载",只需更新 @State posts 数组,列表自动重绘,无需手写刷新。

十一、沉浸式状态栏与模板化总结

首页沉浸式实现里有个容易被忽略的关键点:setWindowSystemBarProperties 把状态栏文字色设成深色 #1C2333,而首页从上到下是"白 Header → 玫红 HeroCard → 白卡片流"的交替底色。状态栏文字位于 Header 白色区域上方(Header 的 safeTop padding 把标题推到状态栏下方),深色文字在白色背景上清晰可见;HeroCard 的玫红渐变则因 safeTop 补偿而与状态栏文字互不重叠。这种"状态栏文字统一深色 + 顶部用白色 Header 承载状态栏区域"的做法,是深色文字状态栏的标准方案;反之若顶部是深色头图,状态栏文字就该设白。理解"状态栏文字色跟随顶部底色"这条规则,沉浸式适配就完成了一半。

配色上,#DB2777(玫红)+ #F472B6(浅粉)的渐变,是典型的"二次元少女感"配色,配合 emoji(🌸🎮🎨)营造年轻化氛围。相比 11 号快递的橙色 #EA580C(工具感、效率感),玫红更"情感、浪漫、二次元"。主题色选择本身就是产品定位的表达——工具应用偏冷色/中性色,内容社区偏暖色/品牌色。这也是本系列"每应用一个主题色"的设计用意:让读者通过颜色差异,直观感受不同应用的调性。

最后把首页抽象成模板:内容社区首页的通用骨架是沉浸式 Header + 情感化 HeroCard(Slogan + 三栏统计 + 双按钮)+ 搜索栏 + 四宫格入口 + 趋势图表 + 内容信息流 + 分类排行。这套骨架的每块"积木"都值得抽进组件库(HeroCardSearchBarQuickActionGridWeekBarChartCardListRankList),换主题色 C.primary、换数据源、换文案,就能拼出任意一个内容社区首页。系列化 demo 的终极价值,正在于把"一次性页面"沉淀成"可复用模板"。把这一页吃透,你就同时掌握了"内容社区首页"与"沉浸式安全区适配"两个高频模板,足以支撑起一个完整的信息流首页。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐