基于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构建 → 状态管理 → 数据持久化 → 网络通信 → 云服务集成 → 性能优化