用HarmonyOS ArkUI来开发一个健康饮食应用
应用'健康食谱',获取个性化健康饮食指导 #生活技巧# #健康生活方式# #健康生活方式书籍# #健康生活方式应用#
本文演示如果在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
AI 代码解读
},
{
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
AI 代码解读
},
{
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
AI 代码解读
},
{
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
AI 代码解读
},
{
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
AI 代码解读
},
{
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
AI 代码解读
},
{
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
AI 代码解读
},
{
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
AI 代码解读
},
{
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
AI 代码解读
},
{
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
AI 代码解读
},
{
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
AI 代码解读
},
{
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
AI 代码解读
}
]
复制
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 })
AI 代码解读
}
}
@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%') }
AI 代码解读
}
}
复制
运行应用,可以看到列表的效果如下。
构建食物详情页面
在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() })
AI 代码解读
}
}
@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')
AI 代码解读
}
}
@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) }
AI 代码解读
}
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 })
AI 代码解读
}
}
@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)
AI 代码解读
}
}
复制
上述代码引用了路由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 })
AI 代码解读
}
}
复制
其中,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
网址:用HarmonyOS ArkUI来开发一个健康饮食应用 https://www.yuejiaxmz.com/news/view/870009
相关内容
用 HarmonyOS ArkUI 来开发一个健康饮食应用HarmonyOS ArkUI入门训练营之健康饮食应用
4天带你上手HarmonyOS ArkUI开发——《HarmonyOS ArkUI入门训练营之健康生活实战》
基于 HarmonyOS Next 和 ArkUI 开发一款 健康生活APP
鸿蒙HarmonyOS应用开发
HarmonyOS实战开发:Divider(基础组件)
HarmonyOS 移动应用开发技术
鸿蒙(HarmonyOS)应用开发——健康生活应用(4)
鸿蒙(HarmonyOS)应用开发——健康生活应用(5)
HarmonyOS Next 智能家居控制系统实战开发