基于HarmonyOS Next的美食发现应用开发实战
基于HarmonyOS Next的美食发现应用开发实战
一、项目概述与环境配置
我们将开发一个名为美味寻踪的美食发现应用,核心功能包括:
- 美食信息瀑布流展示
- 智能收藏与本地存储
- 动态详情页跳转
- 夜间模式切换
开发环境:
- DevEco Studio 4.1 Beta
- HarmonyOS SDK 5.0
- 目标设备:HarmonyOS Next API 11
二、核心功能实现
1. 首页美食瀑布流(ArkTS + Flex布局)
// components/FoodList.ets
@Component
struct FoodItem {
@Prop foodData: Food // 接收父组件传递的美食数据
build() {
Column() {
// 美食封面图
Image(this.foodData.imageUrl)
.width('100%')
.aspectRatio(1.5) // 固定宽高比
.objectFit(ImageFit.Cover) // 裁剪填充
// 美食信息区域
Column({ space: 5 }) {
Text(this.foodData.name)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Row() {
Image($r('app.media.ic_star')) // 星级图标
.width(16)
Text(this.foodData.rating.toFixed(1))
.fontColor('#FF9500')
}
Text('¥' + this.foodData.price)
.fontColor('#FF2D55')
}
.padding(10)
}
.borderRadius(12)
.backgroundColor(Color.White)
.shadow({ radius: 6, color: '#20000000' })
}
}
@Entry
@Component
struct FoodDiscoveryPage {
// 模拟网络请求获取数据
@State foodList: Array<Food> = [
{ id: 1, name: '川味麻辣火锅', rating: 4.8, price: 128, imageUrl: $r('app.media.hotpot') },
{ id: 2, name: '法式焦糖布丁', rating: 4.5, price: 36, imageUrl: $r('app.media.pudding') }
]
build() {
Scroll() {
WaterFlow() {
ForEach(this.foodList, (item: Food) => {
FoodItem({ foodData: item })
.margin({ bottom: 16, right: 8 })
})
}
.columnsTemplate("1fr 1fr") // 两列布局
.padding(12)
}
.backgroundColor('#F5F5F5')
}
}
2. 详情页与路由跳转
// navigation/Router.ets
import router from **********'
export function navigateToDetail(foodId: number) {
router.pushUrl({
url: 'pages/FoodDetail',
params: { id: foodId } // 传递美食ID
})
}
// pages/FoodDetail.ets
@Entry
@Component
struct FoodDetailPage {
@State foodDetail: Food | null = null
onPageShow() {
const foodId = router.getParams()?.['id']
// 根据ID获取详情数据(实际项目调用API)
this.loadDetail(foodId)
}
private loadDetail(id: number) {
// 模拟数据请求
this.foodDetail = {
id: 1,
name: '川味麻辣火锅',
ingredients: ['牛油', '辣椒', '花椒', '牛肉片', '豆腐'],
steps: ['1. 热锅下牛油...', '2. 加入香料炒香...'],
rating: 4.8,
price: 128
}
}
build() {
Column() {
// 顶部返回栏
Row() {
Image($r('app.media.ic_back'))
.onClick(() => router.back())
Text('美食详情').fontSize(20)
}
.padding(12)
// 详情内容展示...
}
}
}
3. 本地收藏功能(Preferences持久化)
// utils/StorageUtil.ets
import preferences from **********'
const COLLECTION_KEY = 'food_collections'
export async function saveCollections(ids: number[]) {
try {
const prefs = await preferences.getPreferences(globalThis.context, 'foodData')
await prefs.put(COLLECTION_KEY, JSON.stringify(ids))
await prefs.flush()
} catch (err) {
console.error('保存收藏失败: ' + err)
}
}
export async function loadCollections(): Promise<number[]> {
try {
const prefs = await preferences.getPreferences(globalThis.context, 'foodData')
const data = await prefs.get(COLLECTION_KEY, '[]')
return JSON.parse(data as string)
} catch {
return []
}
}
// 在详情页使用
@Component
struct CollectButton {
@State isCollected: boolean = false
private foodId: number = 0
aboutToAppear() {
this.foodId = router.getParams()?.['id']
loadCollections().then(ids => {
this.isCollected = ids.includes(this.foodId)
})
}
build() {
Button(this.isCollected ? '已收藏' : '收藏')
.onClick(() => {
this.isCollected = !this.isCollected
// 更新本地存储
updateCollections(this.foodId, this.isCollected)
})
}
private updateCollections(id: number, add: boolean) {
loadCollections().then(ids => {
let newIds = add ? [...ids, id] : ids.filter(i => i !== id)
saveCollections(newIds)
})
}
}
4. 全局夜间模式(AppStorage状态管理)
// constants/GlobalState.ets
export const AppStorageKeys = {
DARK_MODE: 'isDarkMode'
}
AppStorage.setOrCreate<boolean>(AppStorageKeys.DARK_MODE, false)
// 在首页监听模式变化
@Entry
@Component
struct MainPage {
@StorageLink(AppStorageKeys.DARK_MODE) isDarkMode: boolean = false
build() {
Column() {
// 顶部切换按钮
Toggle({ type: ToggleType.Checkbox, isOn: this.isDarkMode })
.onChange(value => {
this.isDarkMode = value
})
// 内容区域
FoodDiscoveryPage()
.backgroundColor(this.isDarkMode ? '#333' : '#F5F5F5')
}
}
}
// 在任意组件获取状态
@Component
struct CustomComponent {
@StorageProp(AppStorageKeys.DARK_MODE) isDarkMode: boolean
build() {
Text('当前模式: ' + (this.isDarkMode ? '夜间' : '日间'))
.fontColor(this.isDarkMode ? Color.White : Color.Black)
}
}
三、AppGallery Connect集成
1. 云端数据接入(以获取实时美食数据为例)
// cloud/FoodService.ets
import agconnect from '@hw-agconnect/api'
import '@hw-agconnect/cloud'
export class FoodService {
// 获取首页推荐列表
static async getRecommendFoods(): Promise<Food[]> {
try {
const result = await agconnect.cloud().callFunction({
name: 'getFoodList',
params: { type: 'recommend' }
})
return result.getValue()?.list || []
} catch (err) {
console.error('云端数据获取失败: ' + JSON.stringify(err))
return []
}
}
}
// 在页面中调用
async loadData() {
this.foodList = await FoodService.getRecommendFoods()
}
2. 用户行为分析(埋点统计)
// utils/AnalyticsUtil.ets
import hiAnalytics from '@hw-hianalytics/analytics'
export function logEvent(eventId: string, params?: object) {
hiAnalytics.onEvent(eventId, params)
}
// 使用示例 - 记录收藏行为
logEvent('collect_action', {
food_id: currentFoodId,
action_type: isCollected ? 'add' : 'remove'
})
四、性能优化技巧
- 图片懒加载
Image(this.foodData.imageUrl)
.syncLoad(false) // 启用异步加载
.placeholder($r('app.media.img_loading')) // 占位图
- 列表项复用优化
ForEach(this.foodList,
(item) => FoodItem({ foodData: item }),
(item) => item.id.toString() // 设置唯一键
)
- 组件按需加载
LazyForEach(this.dataSource,
(item) => FoodItem({ foodData: item }),
(item) => item.id.toString()
)
五、项目扩展建议
- 集成AGC认证服务实现用户系统
- 使用HarmonyOS分布式能力实现跨设备收藏同步
- 添加AI菜品识别功能(集成HMS ML Kit)
- 实现3D美食模型展示(通过XComponent+OpenGL)
最佳实践提示:
- 使用
@Observed和@ObjectLink管理复杂对象状态- 对网络请求进行防抖处理避免重复刷新
- 使用
worker线程处理耗时操作(如本地数据加密)
本篇教程通过完整的项目案例,覆盖了HarmonyOS Next应用开发的核心能力链:
UI构建 → 状态管理 → 数据持久化 → 网络通信 → 云服务集成 → 性能优化

查看18道真题和解析