用HarmonyOS ArkUI来开发一个健康饮食应用

简介: 本文演示如果在DevEco Studio 3里面,用HarmonyOS的ArkUI来开发一个健康饮食应用。体验HarmonyOS 3最新API 9!

本文演示如果在DevEco Studio 3里面,用HarmonyOS的ArkUI来开发一个健康饮食应用。体验HarmonyOS 3最新API 9!

获取HarmonyOS应用
HarmonyOS的ArkUI来开发一个健康饮食的ArkUI程序“ArkUIHealthyDiet”,基础代码已经有了[1],个人只需要在基础代码上稍作修改,就能运行了。

通过DevEco Studio 3导入应用
有关DevEco Studio 3的安装配置,可以参考前文《玩转HarmonyOS 3必装DevEco Studio 3,注意避弹[2]》这里就不在赘述。

首选是打开DevEco Studio 3,可以看到如下界面。

点击“Open Project”来导入我们实行装备好的ArkUI程序“ArkUIHealthyDiet”。

导入程序之后,就能在该程序基础上进行代码开发、运行。

通过DevEco Studio 3创建应用
如果想从0开始学习ArkUI,体验完整的HarmonyOS的开发过程,那么建议跟随本文一起来开启ArkUI开发之旅吧。

首选是打开DevEco Studio 3,可以看到如下界面。

点击“Create Project”来创建ArkUI程序“ArkUIHealthyDiet”。

选择模板
选择空模板Empty Ability,点击“Next”执行下一步。

配置项目
配置项目信息,重要是以下圈中部分。其他配置按照默认配置即可。点击“Finish”执行下一步。

运行HarmonyOS应用
打开Device Manager

登入华为账号
点击“Sign In”登入个人注册的华为账号。如果没有,则参考本文最后的链接进行注册。

启动远程模拟器

运行应用
点击下命的三角形按钮以启动应用

应用运行效果图如下。

完善应用
接下来是进入正题,开始我们的健康饮食应用的核心功能的开发了。

构建食物数据模型
要创建食物数据模型来统一存储和管理食物的数据。食物的信息包括:食物名称、卡路里、蛋白质、脂肪、碳水和维生素C等。

在ets目录下新建model文件夹,用于存放数据模型文件。

在model目录下创建DataModels.ets,用于存放数据模型。

定义食物数据的存储模型FoodInfo和枚举变量CategoryId,FoodData类包含食物id、名称(name)、分类(category)、图片(image)、热量(calories)、蛋白质(protein)、脂肪(fat)、碳水(carbohydrates)和维生素C(vitaminC)属性等等。

export enum CategoryId {
Fruit = 0,
Vegetable,
Nut,
Seafood,
Dessert
}

export type FoodInfo = {
id: number
letter: string
name: string | Resource
image: Resource
categoryId: CategoryId
calories: number
protein: number
fat: number
carbohydrates: number
vitaminC: number
}
复制

创建食物资源数据。在ets目录下创建mock文件夹,并在mock文件夹下创建MockData.ets。在MockData.ets中声明食物成分数组代码如下:

import { FoodInfo, CategoryId} from '../model/DataModels'

// 构造数据的mock数据
export let mockFoods: Array = [
{

id: 0,
letter: 'Kiwi',
name: $r('app.string.food_name_kiwi'),
image: $r('app.media.kiwi'),
categoryId: CategoryId.Fruit,
calories: 61,
protein: 0.8,
fat: 0.6,
carbohydrates: 14.5,
vitaminC: 62

},
{

id: 1,
letter: 'Walnut',
name: $r('app.string.food_name_walnut'),
image: $r('app.media.walnut'),
categoryId: CategoryId.Nut,
calories: 646,
protein: 14.9,
fat: 58.8,
carbohydrates: 19.1,
vitaminC: 1.0

},
{

id: 2,
letter: 'Cucumber',
name: $r('app.string.food_name_cucumber'),
image: $r('app.media.cucumber'),
categoryId: CategoryId.Vegetable,
calories: 16,
protein: 0.8,
fat: 0.2,
carbohydrates: 2.9,
vitaminC: 9.0

},
{

id: 3,
letter: 'Blueberry',
name: $r('app.string.food_name_blueberry'),
image: $r('app.media.blueberry'),
categoryId: CategoryId.Fruit,
calories: 57,
protein: 0.7,
fat: 0.3,
carbohydrates: 14.5,
vitaminC: 9.7

},
{

id: 4,
letter: 'Crab',
name: $r('app.string.food_name_crab'),
image: $r('app.media.crab'),
categoryId: CategoryId.Seafood,
calories: 97,
protein: 19,
fat: 1.5,
carbohydrates: 0,
vitaminC: 7.6

},
{

id: 5,
letter: 'IceCream',
name: $r('app.string.food_name_ice_cream'),
image: $r('app.media.icecream'),
categoryId: CategoryId.Dessert,
calories: 150,
protein: 3.5,
fat: 11,
carbohydrates: 24,
vitaminC: 0.6

},
{

id: 6,
letter: 'Onion',
name: $r('app.string.food_name_onion'),
image: $r('app.media.onion'),
categoryId: CategoryId.Vegetable,
calories: 40,
protein: 1.1,
fat: 0.2,
carbohydrates: 9,
vitaminC: 8.0

},
{

id: 7,
letter: 'Mushroom',
name: $r('app.string.food_name_mushroom'),
image: $r('app.media.mushroom'),
categoryId: CategoryId.Vegetable,
calories: 20,
protein: 3.1,
fat: 0.3,
carbohydrates: 3.3,
vitaminC: 206

},
{

id: 8,
letter: 'Tomato',
name: $r('app.string.food_name_tomato'),
image: $r('app.media.tomato'),
categoryId: CategoryId.Vegetable,
calories: 15,
protein: 0.9,
fat: 0.2,
carbohydrates: 3.3,
vitaminC: 14.0

},
{

id: 9,
letter: 'Pitaya',
name: $r('app.string.food_name_pitaya'),
image: $r('app.media.pitaya'),
categoryId: CategoryId.Fruit,
calories: 55,
protein: 1.1,
fat: 0.2,
carbohydrates: 13.3,
vitaminC: 3.0

},
{

id: 10,
letter: 'Avocado',
name: $r('app.string.food_name_avocado'),
image: $r('app.media.avocado'),
categoryId: CategoryId.Fruit,
calories: 171,
protein: 2.0,
fat: 15.3,
carbohydrates: 7.4,
vitaminC: 8.0

},
{

id: 11,
letter: 'Strawberry',
name: $r('app.string.food_name_strawberry'),
image: $r('app.media.strawberry'),
categoryId: CategoryId.Fruit,
calories: 32,
protein: 1.0,
fat: 0.2,
carbohydrates: 7.1,
vitaminC: 47.0

}
]
复制

name需要考虑国际化,因此,该值是存储在string.json文件中。

image所引用的食物图片资源,放置在resources >base> media目录下。

在model目录下创建DataUtil.ets,用于加载健康饮食应用的数据。

import { FoodInfo } from './DataModels'
import { mockFoods } from '../mock/MockData'

export function getFoods(): Array {
return mockFoods
}
复制

已完成好健康饮食应用的数据资源准备,接下来将通过加载这些数据来创建食物列表页面。

构建食物列表List布局
使用List组件和ForEach循环渲染,构建食物列表布局。

修改pages目录下的Index.ets文件,新建FoodList组件作为页面入口组件,FoodListItem为其子组件。List组件是列表组件,适用于重复同类数据的展示,其子组件为ListItem,适用于展示列表中的单元。

import { FoodInfo } from '../model/DataModels'
import { getFoods } from '../model/DataUtil'

@Component
struct FoodListItem {
private foodItem: FoodInfo

build() {

Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
  Image(this.foodItem.image)
    .objectFit(ImageFit.Contain)
    .height(40)
    .width(40)
    .margin({ right: 16 })
  Text(this.foodItem.name)
    .fontSize(14)
    .flexGrow(1)
  Text(this.foodItem.calories + ' kcal')
    .fontSize(14)
}
.height(64)
.margin({ right: 24, left: 32 })

}
}

@Entry
@Component
struct FoodList {
private foodItems: FoodInfo[] = getFoods()

build() {

Column() {
  Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
    Text('Food List')
      .fontSize(20)
      .margin({ left: 20 })
  }
  .height('7%')
  .backgroundColor('#FFf1f3f5')

  List() {
    ForEach(this.foodItems, item => {
      ListItem() {
        FoodListItem({ foodItem: item })
      }
    }, item => item.id.toString())
  }
  .height('93%')
}

}
}
复制

运行应用,可以看到列表的效果如下。

构建食物详情页面
在pages目录下,创建FoodDetail.ets文件,FoodDetail页面的食物信息都是直接声明的常量,现在要用传递来的FoodData数据来对其进行重新赋值。整体的FoodDetail.ets代码如下。

import router from '@ohos.router'
import { FoodInfo } from '../model/DataModels'

@Component
struct PageTitle {
build() {

Flex({ alignItems: ItemAlign.Start }) {
  Image($r('app.media.back'))
    .width(21.8)
    .height(19.6)
  Text('Food Detail')
    .fontSize(21.8)
    .margin({left: 17.4})
}
.height(61)
.backgroundColor('#FFedf2f5')
.padding({ top: 13, bottom: 15, left: 28.3 })
.onClick(() => {
  router.back()
})

}
}

@Component
struct FoodImageDisplay {
private foodItem: FoodInfo
build() {

Stack({ alignContent: Alignment.BottomStart }) {
  Image(this.foodItem.image)
    .objectFit(ImageFit.Contain)
  Text(this.foodItem.name)
    .fontSize(26)
    .fontWeight(500)
    .margin({ left: 26, bottom: 17.4 })
}
.height(357)
.backgroundColor('#FFedf2f5')

}
}

@Component
struct ContentTable {
private foodItem: FoodInfo

@Builder IngredientItem(title:string, name: string, value: string) {

Flex() {
  Text(title)
    .fontSize(17.4)
    .fontWeight(FontWeight.Bold)
    .layoutWeight(1)
  Flex() {
    Text(name)
      .fontSize(17.4)
      .flexGrow(1)
    Text(value)
      .fontSize(17.4)
  }
  .layoutWeight(2)
}

}

build() {

Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Start }) {
  this.IngredientItem('Calories', 'Calories', this.foodItem.calories + 'kcal')
  this.IngredientItem('Nutrition', 'Protein', this.foodItem.protein + 'g')
  this.IngredientItem('', 'Fat', this.foodItem.fat + 'g')
  this.IngredientItem('', 'Carbohydrates', this.foodItem.carbohydrates + 'g')
  this.IngredientItem('', 'VitaminC', this.foodItem.vitaminC + 'mg')
}
.height(280)
.padding({ top: 30, right: 30, left: 30 })

}
}

@Entry
@Component
struct FoodDetail {
private foodItem: FoodInfo = router.getParams()[foodInfo]

build() {

Column() {
  Stack( { alignContent: Alignment.TopStart }) {
    FoodImageDisplay({ foodItem: this.foodItem })
    PageTitle()
  }
  ContentTable({ foodItem: this.foodItem })
}
.alignItems(HorizontalAlign.Center)

}
}
复制

上述代码引用了路由Router API的接口,通过在页面上引入router,可以调用router的各种接口,从而实现页面路由的各种操作。调用router.getParams()[foodInfo]来获取到列表页面跳转来时携带的foodData对应的数据。

列表与详情页面的跳转
上述详情页面已经引用了路由Router API,能否接受来自路由的参数。那么相应的,列表页面也需要做相应的调整,来触发路由跳转。点击Index后跳转到FoodDetail页面。在FoodListItem内创建Navigator组件,使其子组件都具有路由功能,目标页面target为'pages/FoodDetail'。

修改Index.ets文件,

@Component
struct FoodListItem {
private foodItem: FoodInfo

build() {

// 增加路由导航
Navigator({ target: 'pages/FoodDetail' }) {
  Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
    Image(this.foodItem.image)
      .objectFit(ImageFit.Contain)
      .height(40)
      .width(40)
      .backgroundColor('#FFf1f3f5')
      .margin({ right: 16 })
    Text(this.foodItem.name)
      .fontSize(14)
      .flexGrow(1)
    Text(this.foodItem.calories + ' kcal')
      .fontSize(14)
  }
  .height(64)
}
// 页面间数据传递
.params({ foodInfo: this.foodItem })
.margin({ right: 24, left:32 })

}
}
复制

其中,Navigator为路由容器组件,包装了页面路由的能力,指定页面target后,使其包裹的子组件都具有路由能力。.params方法用于页面间数据传递。

程序运行效果

完整演示视频见B站:https://www.bilibili.com/video/BV1XM411k7iS/

源码
见 https://github.com/waylau/harmonyos-tutorial 中的“ArkUIHealthyDiet”

相关问题
问题1:路由失效
报错如下:

[manifest_router.cpp(GetPagePath)-(0)] [Engine Log] can't find this page pages/FoodDetail path

解决方案:

main_pages中添加pages/FoodDetail

参考引用
《跟老卫学HarmonyOS开发》 开源免费教程,https://github.com/waylau/harmonyos-tutorial ↑

玩转HarmonyOS 3必装DevEco Studio 3,注意避弹 https://developer.huawei.com/consumer/cn/forum/topic/0202103558349879153?fid=0101610563345550409

《鸿蒙 HarmonyOS 应用开发从入门到精通战》(柳伟卫著,北京大学出版社)双十二满100减50

目录
相关文章
|
9天前
「Mac畅玩鸿蒙与硬件41」UI互动应用篇18 - 多滑块联动控制器
本篇将带你实现一个多滑块联动的控制器应用。用户可以通过拖动多个滑块,动态控制不同参数(如红绿蓝三色值),并实时显示最终结果。我们将以动态颜色调节为例,展示如何结合状态管理和交互逻辑,打造一个高级的滑块控制器应用。
143 78
「Mac畅玩鸿蒙与硬件41」UI互动应用篇18 - 多滑块联动控制器
|
10天前
|
UED
「Mac畅玩鸿蒙与硬件40」UI互动应用篇17 - 照片墙布局
本篇将带你实现一个简单的照片墙布局应用,通过展示多张图片组成照片墙效果,用户可以点击图片查看其状态变化。
116 67
「Mac畅玩鸿蒙与硬件40」UI互动应用篇17 - 照片墙布局
|
15天前
|
存储 UED
「Mac畅玩鸿蒙与硬件37」UI互动应用篇14 - 随机颜色变化器
本篇将带你实现一个随机颜色变化器应用。用户点击“随机颜色”按钮后,界面背景会随机变化为淡色系颜色,同时显示当前的颜色代码,页面还会展示一只猫咪图片作为装饰,提升趣味性。
69 36
「Mac畅玩鸿蒙与硬件37」UI互动应用篇14 - 随机颜色变化器
|
12天前
「Mac畅玩鸿蒙与硬件38」UI互动应用篇15 - 猜数字增强版
本篇将带你实现一个升级版的数字猜谜游戏。相比基础版,新增了计分和历史记录功能,用户可以在每次猜测后查看自己的得分和猜测历史。此功能展示了状态管理的进阶用法以及如何保存和显示历史数据。
68 31
「Mac畅玩鸿蒙与硬件38」UI互动应用篇15 - 猜数字增强版
|
7天前
「Mac畅玩鸿蒙与硬件43」UI互动应用篇20 - 闪烁按钮效果
本篇将带你实现一个带有闪烁动画的按钮交互效果。通过动态改变按钮颜色,用户可以在视觉上感受到按钮的闪烁效果,提升界面互动体验。
55 19
「Mac畅玩鸿蒙与硬件43」UI互动应用篇20 - 闪烁按钮效果
|
8天前
「Mac畅玩鸿蒙与硬件42」UI互动应用篇19 - 数字键盘应用
本篇将带你实现一个数字键盘应用,支持用户通过点击数字键输入数字并实时更新显示内容。我们将展示如何使用按钮组件和状态管理来实现一个简洁且实用的数字键盘。
51 17
「Mac畅玩鸿蒙与硬件42」UI互动应用篇19 - 数字键盘应用
|
15天前
|
UED 开发者
「Mac畅玩鸿蒙与硬件36」UI互动应用篇13 - 数字滚动抽奖器
本篇将带你实现一个简单的数字滚动抽奖器。用户点击按钮后,屏幕上的数字会以滚动动画的形式随机变动,最终显示一个抽奖数字。这个项目展示了如何结合定时器、状态管理和动画实现一个有趣的互动应用。
67 23
「Mac畅玩鸿蒙与硬件36」UI互动应用篇13 - 数字滚动抽奖器
|
11天前
|
前端开发 UED
「Mac畅玩鸿蒙与硬件39」UI互动应用篇16 - 倒计时环形进度条
本篇将带你实现一个倒计时环形进度条应用。用户可以设置倒计时的时间,启动倒计时后,应用会动态显示一个随着时间递减的环形进度条,同时伴有数字倒计时显示。这是结合动画效果和时间管理的实用示例。
95 10
「Mac畅玩鸿蒙与硬件39」UI互动应用篇16 - 倒计时环形进度条
|
16天前
「Mac畅玩鸿蒙与硬件35」UI互动应用篇12 - 简易日历
本篇将带你实现一个简易日历应用,显示当前月份的日期,并支持选择特定日期的功能。用户可以通过点击日期高亮选中,还可以切换上下月份,体验动态界面的交互效果。
43 12
「Mac畅玩鸿蒙与硬件35」UI互动应用篇12 - 简易日历
|
22天前
|
UED
「Mac畅玩鸿蒙与硬件27」UI互动应用篇4 - 猫与灯的互动应用
本篇将带领你实现一个趣味十足的互动应用,用户点击按钮时猫会在一排灯之间移动,猫所在的位置灯会亮起(on),其余灯会熄灭(off)。应用会根据用户的操作动态更新灯光状态和文本提示当前亮灯的位置,是掌握状态管理和组件动态渲染的良好实践。
56 15
「Mac畅玩鸿蒙与硬件27」UI互动应用篇4 - 猫与灯的互动应用