OpenHarmony 英语学习 App 实战:ArkUI 卡片式学习首页与响应式布局设计

摘要

学习类 App 的首页,不应该只是功能入口的堆叠。一个好的首页应该能告诉用户:今天学什么、当前进度怎样、下一步从哪里开始。📚✨

本文以我的 OpenHarmony/HarmonyOS 英语学习项目「英语视界 YingYu」为例,分享如何使用 ArkTS + ArkUI 设计一个卡片式学习首页。文章会结合项目中的 HomeContent.ets,讲解首页的数据加载、每日推荐、快捷入口、年级切换、响应式适配和入场动画。

一、首页设计目标

「英语视界」包含词汇学习、听力练习、每日一句、趣味英语、语法专题、短语学习、复习中心、自定义生词本等模块。如果首页只是把这些入口全部摆出来,用户会觉得复杂。

所以首页设计目标是:

  • 第一屏展示用户当前学习状态;
  • 每天给出一个自然推荐内容;
  • 高频入口横向排列,方便快速进入;
  • 手机和平板共用一套组件;
  • 用轻量动画降低页面进入的生硬感。

项目首页对应文件是:

entry/src/main/ets/pages/HomeContent.ets

二、首页状态设计

首页中需要维护多个状态:每日一句、当前年级、学习统计、趣味英语推荐、听力推荐、设备类型等。

@State dailySlides: DailySentence[] = []
@State recommendSwiperIndex: number = 0
@State gradeName: string = ''
@State gradeLevel: number = 3
@State showGradePicker: boolean = false
@State totalWords: number = 0
@State consecutiveDays: number = 0
@State achievementsCount: number = 0
@State homeEnterOpacity: number = 0
@State homeEnterY: number = 20

@State dailyFunItem: FunEnglishItem | null = null
@State dailyListening: ListeningItem | null = null
@State isTabletDevice: boolean = false

这些状态可以分为三类:

  • 内容状态:dailySlidesdailyFunItemdailyListening
  • 用户状态:gradeLeveltotalWordsconsecutiveDays
  • UI 状态:showGradePickerhomeEnterOpacityhomeEnterYisTabletDevice

状态拆得清楚后,页面构建会更稳定。

三、aboutToAppear:进入页面时加载数据

首页在 aboutToAppear() 中加载数据,并执行入场动画。

aboutToAppear() {
  this.loadData()
  this.homeEnterOpacity = 0
  this.homeEnterY = 20
  this.isTabletDevice = isTablet()

  animateTo({
    duration: 520,
    curve: Curve.EaseOut
  }, () => {
    this.homeEnterOpacity = 1
    this.homeEnterY = 0
  })
}

这里的动画逻辑很简单:页面从透明、下移 20 的状态过渡到正常位置。

.opacity(this.homeEnterOpacity)
.translate({ y: this.homeEnterY })

学习类 App 不适合很夸张的动效,这种轻微上浮的入场动画刚好能提升质感。

四、首页数据加载:把推荐内容和用户状态组合起来

loadData() 是首页的数据核心。它负责加载每日推荐、用户设置、学习统计、趣味英语和听力内容。

loadData() {
  try {
    this.dailySlides = getDailyRecommendSentences(this.recommendSlideCount)
    this.recommendSwiperIndex = 0

    const settings = getUserSettings()
    this.gradeLevel = settings.gradeLevel
    const gradeInfo = gradeInfoList.find((g: GradeInfo) => g.level === settings.gradeLevel)
    this.gradeName = gradeInfo ? gradeInfo.name : '小学三年级'

    const stats = getStatistics()
    this.totalWords = stats.totalWords
    this.consecutiveDays = stats.consecutiveDays
    this.achievementsCount = stats.achievementsCount

    this.dailyFunItem = getRandomFunEnglishItem()
  } catch (e) {
    console.error('Home loadData error:', JSON.stringify(e))
  }
}

这里有一个很重要的思路:首页不直接计算所有数据,而是调用工具函数获取结果

例如:

  • getUserSettings() 获取用户年级和目标;
  • getStatistics() 获取学习统计;
  • getDailyRecommendSentences() 获取每日一句;
  • getRandomFunEnglishItem() 获取趣味英语推荐。

这样页面层就不会变成业务逻辑的大杂烩。

五、根据年级推荐听力难度

项目中还根据用户年级自动选择听力难度:

const settings2 = getUserSettings()
const gradeLvl = settings2.gradeLevel
let diffLevel = 1
if (gradeLvl <= 3) diffLevel = 1
else if (gradeLvl <= 6) diffLevel = 2
else if (gradeLvl <= 9) diffLevel = 3
else diffLevel = 4

const listenItems = getListeningItemsByDifficulty(diffLevel)

这个逻辑很适合教育类产品:

  • 小学低年级:简单句;
  • 小学高年级:短对话;
  • 初中:中等材料;
  • 高中:更复杂文本。

推荐内容不需要一开始就用复杂 AI,先用规则就能提升匹配度。

六、页面结构:多个 Builder 组成首页

首页主体结构很清晰:

build() {
  Scroll() {
    Column() {
      this.WelcomeSection()
      this.DailyRecommendCard()
      this.QuickAccessSection()
      this.FunEnglishPickSection()
      this.ListeningRecommendSection()
      this.FeatureGrid()
      this.StatisticsCard()
    }
    .width('100%')
    .padding({
      left: getPagePadding(),
      right: getPagePadding(),
      top: 12,
      bottom: 28
    })
    .opacity(this.homeEnterOpacity)
    .translate({ y: this.homeEnterY })
  }
  .width('100%')
  .height('100%')
  .edgeEffect(EdgeEffect.Spring)
}

每个区域都拆成独立 Builder:

  • WelcomeSection():欢迎区和年级选择;
  • DailyRecommendCard():每日一句;
  • QuickAccessSection():快捷入口;
  • FunEnglishPickSection():趣味英语推荐;
  • ListeningRecommendSection():听力推荐;
  • FeatureGrid():功能网格;
  • StatisticsCard():学习统计。

这种写法比把所有 UI 都堆在 build() 中更好维护。

七、快捷入口:横向滚动降低首页拥挤感

首页功能很多,如果全部放成网格,会占用太多垂直空间。项目使用横向滚动展示快捷入口:

@Builder
QuickAccessScroll() {
  Scroll() {
    Row() {
      this.QuickAccessTile('每日任务', '任务中心', $r('sys.symbol.calendar'), $r('app.color.listening_solid'), () => router.pushUrl({ url: 'pages/DailyTask' }))
      this.QuickAccessTile('复习中心', '艾宾浩斯', $r('sys.symbol.arrow_2_circlepath'), $r('app.color.icon_tag_grade'), () => router.pushUrl({ url: 'pages/ReviewCenter' }))
      this.QuickAccessTile('学习目标', '设定目标', $r('sys.symbol.book'), $r('app.color.icon_tag_goal'), () => router.pushUrl({ url: 'pages/LearningGoals' }))
      this.QuickAccessTile('学习日历', '打卡记录', $r('sys.symbol.calendar'), $r('app.color.primary_color'), () => router.pushUrl({ url: 'pages/LearningCalendar' }))
    }
    .alignItems(VerticalAlign.Center)
  }
  .width('100%')
  .scrollable(ScrollDirection.Horizontal)
  .scrollBar(BarState.Off)
  .edgeEffect(EdgeEffect.Spring)
}

横向入口适合放高频功能:

  • 每日任务;
  • 复习中心;
  • 学习目标;
  • 学习日历;
  • 自定义生词;
  • 语法学习;
  • 短语学习。

用户不需要一次看到所有内容,但可以很快滑动找到入口。

八、年级切换:首页直接影响推荐策略

首页的年级选择不是装饰,而是会影响推荐内容和学习难度。

private applyGradeLevel(level: number): void {
  const prev = getUserSettings()
  const next: UserSettings = {
    gradeLevel: level,
    dailyGoal: prev.dailyGoal,
    notificationsEnabled: prev.notificationsEnabled,
    soundEnabled: prev.soundEnabled
  }

  saveUserSettings(next)
  const gradeInfo = gradeInfoList.find((g: GradeInfo) => g.level === level)
  this.gradeName = gradeInfo ? gradeInfo.name : '小学三年级'
  this.gradeLevel = level
  this.showGradePicker = false
}

这段代码做了三件事:

  1. 保留原来的每日目标、通知和声音设置;
  2. 更新年级;
  3. 关闭选择面板并刷新显示。

对学习 App 来说,年级是非常重要的用户画像字段,应该贯穿词汇、听力、语法、推荐内容。

九、响应式布局:用工具函数统一页面间距

首页没有在每个地方手写固定边距,而是调用 getPagePadding()getSectionGap() 等工具函数。

.padding({
  left: getPagePadding(),
  right: getPagePadding(),
  top: 12,
  bottom: 28
})

同时根据 isTabletDevice 调整字号、圆角和 padding:

Text('欢迎来到英语视界')
  .fontSize(this.isTabletDevice ? 26 : 22)
  .fontWeight(FontWeight.Bold)

.padding(this.isTabletDevice ? 24 : 18)
.borderRadius(this.isTabletDevice ? 24 : 18)

这样一套页面可以同时适配手机和平板,不需要维护两套首页。

十、首页设计经验总结

1. 首页要回答“下一步做什么”

学习类首页不要只展示品牌和 Banner,要把“每日任务”“复习中心”“今日推荐”放在用户最容易看到的位置。

2. 推荐内容要和用户状态相关

年级、学习进度、连续打卡天数都可以影响推荐策略。

3. 卡片式布局要克制

卡片太多会变成信息噪音。建议按优先级排序:状态卡、推荐卡、快捷入口、统计卡。

4. 动效要服务进入感

轻微透明度和位移动画就足够,不要让学习 App 像游戏大厅。

十一、小结

本文结合「英语视界 YingYu」的 HomeContent.ets,拆解了 OpenHarmony/ArkUI 卡片式首页的实现方式:

  • 使用 @State 管理首页内容、用户状态和 UI 状态;
  • aboutToAppear() 中加载数据并执行入场动画;
  • 通过 loadData() 聚合每日推荐、学习统计和听力推荐;
  • 使用多个 Builder 拆分首页模块;
  • 横向滚动展示快捷入口;
  • 年级切换影响推荐策略;
  • 使用响应式工具函数适配手机和平板。

一个好的学习首页,不是把功能放满,而是让用户愿意开始下一次学习。🌱

img

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐