基于HarmonyOS Next的美食发现应用开发实战

基于HarmonyOS Next的美食发现应用开发实战

一、项目概述与环境配置

我们将开发一个名为美味寻踪的美食发现应用,核心功能包括:

  1. 美食信息瀑布流展示
  2. 智能收藏与本地存储
  3. 动态详情页跳转
  4. 夜间模式切换

开发环境

  • 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'
})

四、性能优化技巧

  1. 图片懒加载
Image(this.foodData.imageUrl)
  .syncLoad(false) // 启用异步加载
  .placeholder($r('app.media.img_loading')) // 占位图

  1. 列表项复用优化
ForEach(this.foodList, 
  (item) => FoodItem({ foodData: item }),
  (item) => item.id.toString() // 设置唯一键
)

  1. 组件按需加载
LazyForEach(this.dataSource, 
  (item) => FoodItem({ foodData: item }),
  (item) => item.id.toString()
)

五、项目扩展建议

  1. 集成AGC认证服务实现用户系统
  2. 使用HarmonyOS分布式能力实现跨设备收藏同步
  3. 添加AI菜品识别功能(集成HMS ML Kit)
  4. 实现3D美食模型展示(通过XComponent+OpenGL)

最佳实践提示

  • 使用@Observed@ObjectLink管理复杂对象状态
  • 对网络请求进行防抖处理避免重复刷新
  • 使用worker线程处理耗时操作(如本地数据加密)

本篇教程通过完整的项目案例,覆盖了HarmonyOS Next应用开发的核心能力链

UI构建 → 状态管理 → 数据持久化 → 网络通信 → 云服务集成 → 性能优化

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务