鸿蒙智能购物清单应用开发指南
·
鸿蒙智能购物清单应用开发指南
一、功能概述
本文将介绍如何基于HarmonyOS的AI能力开发一款智能购物清单应用,主要功能包括:
- 拍照识别商品并自动生成购物清单(使用@ohos.ai.objectDetection)
- 支持商品分类和数量识别
- 跨设备同步购物清单
- 多人协同编辑购物清单
二、技术架构
graph TD
A[商品拍照] --> B[AI物体识别]
B --> C[清单生成]
C --> D[分布式数据同步]
D --> E[其他设备]
三、核心代码实现
1. 商品识别模块
import objectDetection from '@ohos.ai.objectDetection';
import image from '@ohos.multimedia.image';
class ProductDetector {
private detector: objectDetection.ObjectDetector;
async initDetector() {
try {
const context = getContext(this) as common.UIAbilityContext;
this.detector = await objectDetection.createObjectDetector(context);
const config: objectDetection.DetectionConfig = {
detectMode: objectDetection.DetectMode.MODE_ACCURATE,
objectType: objectDetection.ObjectType.TYPE_GOODS
};
await this.detector.setConfig(config);
} catch (err) {
console.error(`初始化物体检测器失败: ${err.code}, ${err.message}`);
}
}
async detectProducts(imageSource: image.ImageSource): Promise<Product[]> {
try {
const pixelMap = await imageSource.createPixelMap();
const results = await this.detector.detect(pixelMap);
return this.parseDetectionResults(results);
} catch (err) {
console.error(`商品检测失败: ${err.code}, ${err.message}`);
return [];
}
}
private parseDetectionResults(results: objectDetection.DetectionResult[]): Product[] {
return results.map(result => {
return {
name: result.objectName,
category: this.getCategory(result.objectName),
quantity: this.estimateQuantity(result.boundingBox),
boundingBox: result.boundingBox
};
});
}
private getCategory(productName: string): string {
// 简单分类逻辑 - 实际应用中可以使用更复杂的分类器
const categories: Record<string, string[]> = {
'水果': ['苹果', '香蕉', '橙子', '葡萄'],
'蔬菜': ['胡萝卜', '西红柿', '黄瓜', '土豆'],
'饮品': ['牛奶', '果汁', '啤酒', '矿泉水']
};
for (const [category, items] of Object.entries(categories)) {
if (items.includes(productName)) {
return category;
}
}
return '其他';
}
private estimateQuantity(box: objectDetection.Rect): number {
// 根据物体大小估算数量 - 简化版
const area = box.right - box.left * (box.bottom - box.top);
return area > 0.3 ? 2 : 1; // 面积大于阈值认为是2个
}
}
interface Product {
name: string;
category: string;
quantity: number;
boundingBox?: objectDetection.Rect;
}
2. 分布式购物清单同步
import distributedData from '@ohos.data.distributedData';
import deviceManager from '@ohos.distributedDeviceManager';
class ShoppingListSync {
private kvManager: distributedData.KVManager;
private kvStore: distributedData.KVStore;
private deviceManager: deviceManager.DeviceManager;
async initSyncService() {
const config = {
bundleName: 'com.example.shoppinglist',
userInfo: {
userId: 'currentUser'
}
};
try {
this.kvManager = distributedData.createKVManager(config);
const options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true,
kvStoreType: distributedData.KVStoreType.SINGLE_VERSION,
schema: JSON.stringify({
name: 'ShoppingList',
attributes: {
items: { type: 'array' },
lastModified: { type: 'number' },
modifiedBy: { type: 'string' }
}
})
};
this.kvStore = await this.kvManager.getKVStore('shopping_list', options);
// 初始化设备管理
const DM_ABILITY_NAME = "com.example.shopping.DmAbility";
this.deviceManager = deviceManager.createDeviceManager(DM_ABILITY_NAME);
// 订阅数据变更
this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => {
this.handleListUpdate(data);
});
} catch (err) {
console.error(`初始化同步服务失败: ${err.code}, ${err.message}`);
}
}
async syncList(items: ShoppingItem[]) {
const deviceId = this.deviceManager.getLocalDeviceInfo().deviceId;
const listData = {
items: items,
lastModified: Date.now(),
modifiedBy: deviceId
};
try {
await this.kvStore.put('current_list', JSON.stringify(listData));
} catch (err) {
console.error(`同步购物清单失败: ${err.code}, ${err.message}`);
}
}
private handleListUpdate(data: distributedData.ChangeNotification) {
const remoteData = data.inserted[0] || data.updated[0];
if (remoteData && remoteData.key === 'current_list') {
const listData = JSON.parse(remoteData.value);
const localDeviceId = this.deviceManager.getLocalDeviceInfo().deviceId;
// 忽略自己发出的更新
if (listData.modifiedBy !== localDeviceId) {
this.applyRemoteUpdate(listData.items);
}
}
}
private applyRemoteUpdate(items: ShoppingItem[]) {
// 更新UI或本地数据
console.info('收到远程购物清单更新:', items);
}
}
interface ShoppingItem {
id: string;
name: string;
category: string;
quantity: number;
checked: boolean;
}
3. 主界面实现 (ArkUI)
@Entry
@Component
struct ShoppingListPage {
@State listItems: ShoppingItem[] = [];
@State isScanning: boolean = false;
private detector = new ProductDetector();
private syncService = new ShoppingListSync();
private cameraController: CameraController;
async aboutToAppear() {
await this.detector.initDetector();
await this.syncService.initSyncService();
}
build() {
Column() {
// 标题栏
Row() {
Text('智能购物清单')
.fontSize(24)
.fontWeight(FontWeight.Bold)
Button('拍照添加')
.onClick(() => this.startScanning())
.margin({ left: 20 })
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding(10)
// 分类标签
CategoryTabs({
categories: this.getCategories(),
onCategoryChange: (category) => this.filterByCategory(category)
})
// 购物清单列表
List() {
ForEach(this.listItems, (item) => {
ListItem() {
ShoppingItem({
item: item,
onCheck: (checked) => this.toggleItem(item.id, checked),
onQuantityChange: (qty) => this.updateQuantity(item.id, qty)
})
}
}, item => item.id)
}
.layoutWeight(1)
.width('100%')
// 扫描界面
if (this.isScanning) {
ScannerOverlay({
onCancel: () => this.isScanning = false,
onConfirm: (image) => this.processScannedImage(image)
})
}
}
.height('100%')
}
private getCategories(): string[] {
const categories = new Set<string>();
this.listItems.forEach(item => categories.add(item.category));
return Array.from(categories);
}
private filterByCategory(category: string) {
// 实现分类过滤逻辑
}
private async startScanning() {
this.isScanning = true;
}
private async processScannedImage(image: image.ImageSource) {
this.isScanning = false;
const detectedProducts = await this.detector.detectProducts(image);
const newItems = detectedProducts.map(product => {
return {
id: generateId(),
name: product.name,
category: product.category,
quantity: product.quantity,
checked: false
};
});
this.listItems = this.mergeItems(this.listItems, newItems);
this.syncService.syncList(this.listItems);
}
private mergeItems(existing: ShoppingItem[], newItems: ShoppingItem[]): ShoppingItem[] {
// 合并已有商品和新增商品,相同商品数量累加
const merged = [...existing];
newItems.forEach(newItem => {
const existingItem = merged.find(item => item.name === newItem.name);
if (existingItem) {
existingItem.quantity += newItem.quantity;
} else {
merged.push(newItem);
}
});
return merged;
}
private toggleItem(id: string, checked: boolean) {
this.listItems = this.listItems.map(item => {
if (item.id === id) {
return { ...item, checked };
}
return item;
});
this.syncService.syncList(this.listItems);
}
private updateQuantity(id: string, quantity: number) {
this.listItems = this.listItems.map(item => {
if (item.id === id) {
return { ...item, quantity };
}
return item;
});
this.syncService.syncList(this.listItems);
}
}
// 生成唯一ID
function generateId(): string {
return Math.random().toString(36).substring(2, 9);
}
4. 商品组件实现
@Component
struct ShoppingItem {
@Prop item: ShoppingItem;
@Emit onCheck: (checked: boolean) => void;
@Emit onQuantityChange: (quantity: number) => void;
build() {
Row() {
// 复选框
Checkbox()
.select(this.item.checked)
.onChange(checked => this.onCheck(checked))
.margin({ right: 10 })
// 商品信息
Column() {
Text(this.item.name)
.fontSize(18)
Text(this.item.category)
.fontSize(12)
.fontColor(Color.Gray)
}
.layoutWeight(1)
// 数量控制
QuantityStepper({
quantity: this.item.quantity,
onChange: qty => this.onQuantityChange(qty)
})
}
.padding(10)
.borderRadius(8)
.backgroundColor(this.item.checked ? '#f5f5f5' : '#ffffff')
.opacity(this.item.checked ? 0.6 : 1)
}
}
@Component
struct QuantityStepper {
@State quantity: number = 1;
@Emit onChange: (quantity: number) => void;
build() {
Row() {
Button('-')
.onClick(() => this.changeQuantity(-1))
.width(30)
.height(30)
Text(this.quantity.toString())
.width(40)
.textAlign(TextAlign.Center)
Button('+')
.onClick(() => this.changeQuantity(1))
.width(30)
.height(30)
}
}
changeQuantity(delta: number) {
const newQuantity = Math.max(1, this.quantity + delta);
this.quantity = newQuantity;
this.onChange(newQuantity);
}
}
四、关键优化点
-
AI识别优化:
// 使用更精确的商品识别模型 async initDetector() { const config: objectDetection.DetectionConfig = { detectMode: objectDetection.DetectMode.MODE_ACCURATE, objectType: objectDetection.ObjectType.TYPE_GOODS, modelName: 'enhanced_shopping_model' // 使用优化后的模型 }; await this.detector.setConfig(config); } -
冲突解决策略:
private handleListUpdate(data: distributedData.ChangeNotification) { const remoteData = data.inserted[0] || data.updated[0]; if (remoteData && remoteData.key === 'current_list') { const remoteList = JSON.parse(remoteData.value); const localDeviceId = this.deviceManager.getLocalDeviceInfo().deviceId; // 使用最后修改时间解决冲突 if (remoteList.lastModified > this.lastModified) { this.applyRemoteUpdate(remoteList.items); } } } -
离线支持:
async syncList(items: ShoppingItem[]) { try { // 先保存到本地 await this.kvStore.put('local_list_backup', JSON.stringify(items)); // 尝试同步到网络 if (this.isOnline()) { await this.kvStore.put('current_list', JSON.stringify({ items: items, lastModified: Date.now(), modifiedBy: this.deviceManager.getLocalDeviceInfo().deviceId })); } } catch (err) { console.error('同步失败,已保存本地备份'); } }
五、扩展功能实现
1. 多人协同编辑
// 添加编辑锁机制
async lockListForEditing() {
const deviceId = this.deviceManager.getLocalDeviceInfo().deviceId;
const lockInfo = {
lockedBy: deviceId,
lockedAt: Date.now()
};
try {
await this.kvStore.put('list_lock', JSON.stringify(lockInfo));
return true;
} catch (err) {
console.error('获取编辑锁失败');
return false;
}
}
// 检查列表是否被其他设备编辑
async checkListLock(): Promise<boolean> {
try {
const lockData = await this.kvStore.get('list_lock');
if (lockData) {
const lockInfo = JSON.parse(lockData.toString());
const localDeviceId = this.deviceManager.getLocalDeviceInfo().deviceId;
// 锁不是本设备持有且未超时(30秒)
if (lockInfo.lockedBy !== localDeviceId &&
Date.now() - lockInfo.lockedAt < 30000) {
return true;
}
}
return false;
} catch (err) {
return false;
}
}
2. 智能推荐
// 基于历史购物数据推荐商品
class ShoppingRecommender {
private history: ShoppingHistory[];
async loadHistory() {
try {
const historyData = await this.kvStore.get('shopping_history');
this.history = JSON.parse(historyData.toString()) || [];
} catch (err) {
this.history = [];
}
}
getRecommendations(): RecommendedItem[] {
// 分析历史数据,返回推荐商品
const frequentlyBought = this.analyzeFrequency();
return frequentlyBought.slice(0, 5); // 返回前5个常购商品
}
private analyzeFrequency(): { name: string, frequency: number }[] {
// 实现频率分析逻辑
}
}
// 在UI中显示推荐
@Component
struct RecommendationBar {
@State recommendations: RecommendedItem[] = [];
private recommender = new ShoppingRecommender();
async aboutToAppear() {
await this.recommender.loadHistory();
this.recommendations = this.recommender.getRecommendations();
}
build() {
Scroll() {
Row() {
ForEach(this.recommendations, (item) => {
RecommendationItem({ item: item })
})
}
}
.height(80)
}
}
六、测试方案
1. 商品识别测试
describe('ProductDetector Test', () => {
let detector: ProductDetector;
before(async () => {
detector = new ProductDetector();
await detector.initDetector();
});
it('should detect common grocery items', async () => {
const testImage = await loadTestImage('groceries.jpg');
const products = await detector.detectProducts(testImage);
expect(products.length).toBeGreaterThan(0);
expect(products.some(p => p.name === '苹果')).toBeTruthy();
});
});
2. 分布式同步测试
describe('ShoppingListSync Test', () => {
let syncService: ShoppingListSync;
before(async () => {
syncService = new ShoppingListSync();
await syncService.initSyncService();
});
it('should sync items across devices', async () => {
const testItems: ShoppingItem[] = [
{ id: '1', name: '牛奶', category: '饮品', quantity: 2, checked: false }
];
await syncService.syncList(testItems);
// 模拟远程设备接收
const remoteData = await syncService.kvStore.get('current_list');
const receivedItems = JSON.parse(remoteData.toString()).items;
expect(receivedItems).toEqual(testItems);
});
});
七、总结
本文实现的智能购物清单应用具有以下特点:
- 智能识别:利用@ohos.ai.objectDetection实现高精度商品识别
- 自动分类:基于商品名称自动归类,便于管理
- 无缝协同:通过分布式数据服务实现多设备实时同步
- 灵活交互:支持多人同时编辑和智能推荐
该方案可以轻松扩展为家庭共享购物清单、超市智能导购等场景,展现了HarmonyOS在AI和分布式能力方面的强大优势。开发者可以根据实际需求,进一步优化商品识别算法或增加语音输入等交互方式。
更多推荐

所有评论(0)