鸿蒙微笑打卡应用开发指南 原创

进修的泡芙
发布于 2025-6-22 17:11
浏览
0收藏

鸿蒙微笑打卡应用开发指南

一、系统架构设计

基于HarmonyOS的AI能力和分布式技术,我们设计了一套微笑打卡系统,主要功能包括:
微笑检测:通过摄像头实时检测用户微笑

积分奖励:根据微笑次数和持续时间奖励积分

多设备同步:跨设备同步微笑记录和积分

社交分享:分享微笑成就到社交平台

成就系统:解锁不同微笑成就

!https://example.com/harmony-smile-diary-arch.png

二、核心代码实现
微笑检测服务

// SmileDetectionService.ets
import camera from ‘@ohos.multimedia.camera’;
import image from ‘@ohos.multimedia.image’;
import face from ‘@ohos.ai.face’;

class SmileDetectionService {
private static instance: SmileDetectionService;
private cameraManager: camera.CameraManager;
private faceDetector: face.FaceDetector;
private cameraInput: camera.CameraInput | null = null;
private previewOutput: camera.PreviewOutput | null = null;
private isDetecting: boolean = false;
private smileCount: number = 0;
private lastSmileTime: number = 0;

private constructor() {
this.cameraManager = camera.getCameraManager();
this.faceDetector = face.createFaceDetector();
public static getInstance(): SmileDetectionService {

if (!SmileDetectionService.instance) {
  SmileDetectionService.instance = new SmileDetectionService();

return SmileDetectionService.instance;

public async initCamera(previewSurfaceId: string): Promise<void> {

const cameras = this.cameraManager.getSupportedCameras();
if (cameras.length === 0) {
  throw new Error('No camera available');

this.cameraInput = this.cameraManager.createCameraInput(cameras[0]);

await this.cameraInput.open();

const previewProfile = this.cameraManager.getSupportedOutputCapability(
  cameras[0],
  camera.ProfileMode.PROFILE_MODE_DEFAULT
).previewProfiles[0];

this.previewOutput = this.cameraManager.createPreviewOutput(
  previewProfile,
  previewSurfaceId
);

public startDetection(callback: (smile: boolean, score: number) => void): void {

if (this.isDetecting) return;

this.isDetecting = true;
const detectionOptions = {
  featureType: face.FeatureType.FEATURE_TYPE_SMILE,
  processMode: face.ProcessMode.PROCESS_MODE_FAST
};

const interval = setInterval(async () => {
  if (!this.isDetecting) {
    clearInterval(interval);
    return;

try {

    const image = await this.captureFrame();
    this.faceDetector.detect(image, detectionOptions, (err, result) => {
      if (!err && result?.faces?.length > 0) {
        const smile = result.faces[0].smile;
        callback(smile.detected, smile.score);
        
        if (smile.detected) {
          this.handleSmileDetected(smile.score);

}

    });

catch (error) {

    console.error('Detection error:', error);

}, 500); // 每500ms检测一次

public stopDetection(): void {

this.isDetecting = false;

public getSmileCount(): number {

return this.smileCount;

public resetCounter(): void {

this.smileCount = 0;
this.lastSmileTime = 0;

private async captureFrame(): Promise<image.Image> {

// 简化实现,实际应从预览流获取帧
return new Promise((resolve) => {
  const mockImage = {};
  resolve(mockImage as image.Image);
});

private handleSmileDetected(score: number): void {

const now = Date.now();
// 防止连续检测到同一个微笑
if (now - this.lastSmileTime > 2000) { // 2秒内不重复计数
  this.smileCount++;
  this.lastSmileTime = now;
  
  EventBus.emit('smileDetected', {
    count: this.smileCount,
    score,
    timestamp: now
  });

}

export const smileDetectionService = SmileDetectionService.getInstance();

积分奖励服务

// PointRewardService.ets
class PointRewardService {
private static instance: PointRewardService;
private points: number = 0;
private dailyRecords: DailyRecord[] = [];

private constructor() {}

public static getInstance(): PointRewardService {
if (!PointRewardService.instance) {
PointRewardService.instance = new PointRewardService();
return PointRewardService.instance;

public addSmile(smileScore: number): number {

const pointsEarned = this.calculatePoints(smileScore);
this.points += pointsEarned;

const today = this.getCurrentDate();
let record = this.dailyRecords.find(r => r.date === today);

if (!record) {
  record = { date: today, smiles: [], totalPoints: 0 };
  this.dailyRecords.push(record);

record.smiles.push({

  score: smileScore,
  points: pointsEarned,
  timestamp: Date.now()
});
record.totalPoints += pointsEarned;

EventBus.emit('pointsUpdated', {
  total: this.points,
  daily: record.totalPoints,
  added: pointsEarned
});

this.checkAchievements();

return pointsEarned;

public getTotalPoints(): number {

return this.points;

public getDailyPoints(): number {

const today = this.getCurrentDate();
const record = this.dailyRecords.find(r => r.date === today);
return record?.totalPoints || 0;

private calculatePoints(smileScore: number): number {

// 根据微笑质量计算积分
if (smileScore > 0.8) return 5;
if (smileScore > 0.6) return 3;
if (smileScore > 0.4) return 2;
return 1;

private getCurrentDate(): string {

return new Date().toISOString().split('T')[0];

private checkAchievements(): void {

const achievements = achievementService.checkAchievements(
  this.points,
  this.dailyRecords
);

if (achievements.length > 0) {
  EventBus.emit('achievementUnlocked', achievements);

}

export const pointService = PointRewardService.getInstance();

多设备同步服务

// SmileSyncService.ets
import distributedData from ‘@ohos.data.distributedData’;

class SmileSyncService {
private static instance: SmileSyncService;
private kvManager: distributedData.KVManager;
private kvStore: distributedData.KVStore;

private constructor() {
this.initKVStore();
private async initKVStore(): Promise<void> {

const config = {
  bundleName: 'com.example.smileDiary',
  userInfo: { userId: 'currentUser' }
};

this.kvManager = distributedData.createKVManager(config);
this.kvStore = await this.kvManager.getKVStore('smile_store', {
  createIfMissing: true
});

this.kvStore.on('dataChange', (data) => {
  this.handleRemoteUpdate(data);
});

public static getInstance(): SmileSyncService {

if (!SmileSyncService.instance) {
  SmileSyncService.instance = new SmileSyncService();

return SmileSyncService.instance;

public async syncSmileRecord(record: SmileRecord): Promise<void> {

await this.kvStore.put(smile_${record.id}, JSON.stringify(record));

public async getSmileRecords(date?: string): Promise<SmileRecord[]> {

const entries = await this.kvStore.getEntries('smile_');
let records = Array.from(entries).map(([_, value]) => JSON.parse(value));

if (date) {
  records = records.filter(r => r.date === date);

return records.sort((a, b) => b.timestamp - a.timestamp);

public async syncPoints(totalPoints: number): Promise<void> {

await this.kvStore.put('total_points', JSON.stringify(totalPoints));

public async getTotalPoints(): Promise<number> {

const value = await this.kvStore.get('total_points');
return value ? JSON.parse(value) : 0;

public async syncAchievements(achievements: Achievement[]): Promise<void> {

await this.kvStore.put('achievements', JSON.stringify(achievements));

public async getAchievements(): Promise<Achievement[]> {

const value = await this.kvStore.get('achievements');
return value ? JSON.parse(value) : [];

private handleRemoteUpdate(data: distributedData.ChangeInfo): void {

if (data.deviceId === deviceInfo.deviceId) return;

const key = data.key as string;
if (key.startsWith('smile_')) {
  const record = JSON.parse(data.value);
  EventBus.emit('smileRecordUpdated', record);

else if (key === ‘total_points’) {

  const points = JSON.parse(data.value);
  EventBus.emit('pointsSynced', points);

else if (key === ‘achievements’) {

  const achievements = JSON.parse(data.value);
  EventBus.emit('achievementsSynced', achievements);

}

export const smileSyncService = SmileSyncService.getInstance();

三、主界面实现
微笑检测界面

// SmileCameraView.ets
@Component
struct SmileCameraView {
@State previewSurfaceId: string = ‘’;
@State isDetecting: boolean = false;
@State smileCount: number = 0;
@State smileScore: number = 0;
@State pointsEarned: number = 0;

aboutToAppear() {
this.initCamera();
build() {

Stack() {
  // 相机预览
  XComponent({
    id: 'smileCameraPreview',
    type: 'surface',
    libraryname: 'libcamera.so',
    controller: this.previewController
  })
  .onLoad(() => {
    this.previewSurfaceId = this.previewController.getXComponentSurfaceId();
    smileDetectionService.initCamera(this.previewSurfaceId);
  })
  .width('100%')
  .height('100%')
  
  // 微笑检测UI
  Column() {
    Text('微笑打卡')
      .fontSize(24)
      .fontWeight(FontWeight.Bold)
      .margin({ top: 16 })
    
    Text(今日微笑: ${this.smileCount}次)
      .fontSize(18)
      .margin({ top: 8 })
    
    Text(累计积分: ${pointService.getTotalPoints()})
      .fontSize(18)
      .margin({ top: 8 })
    
    if (this.isDetecting) {
      Text(this.smileScore > 0 ? '😊' : '😐')
        .fontSize(60)
        .margin({ top: 24 })
      
      Text(微笑强度: ${Math.round(this.smileScore * 100)}%)
        .fontSize(16)
        .margin({ top: 8 })
      
      if (this.pointsEarned > 0) {
        Text(+${this.pointsEarned}积分)
          .fontSize(20)
          .fontColor('#4CAF50')
          .margin({ top: 8 })

}

.width(‘100%’)

  .alignItems(HorizontalAlign.Center)
  
  // 控制按钮
  Button(this.isDetecting ? '停止检测' : '开始微笑检测')
    .onClick(() => this.toggleDetection())
    .position({ x: '50%', y: '90%' })

}

private async initCamera(): Promise<void> {
await smileDetectionService.initCamera(this.previewSurfaceId);
private toggleDetection(): void {

if (this.isDetecting) {
  smileDetectionService.stopDetection();

else {

  smileDetectionService.startDetection((smile, score) => {
    this.smileScore = score;
    
    if (smile) {
      this.pointsEarned = pointService.addSmile(score);
      this.smileCount = smileDetectionService.getSmileCount();

});

this.isDetecting = !this.isDetecting;

}

积分排行榜界面

// LeaderboardView.ets
@Component
struct LeaderboardView {
@State friends: FriendScore[] = [];
@State myRank: number = 0;
@State isLoading: boolean = true;

aboutToAppear() {
this.loadLeaderboard();
EventBus.on(‘pointsSynced’, () => this.loadLeaderboard());
build() {

Column() {
  Text('微笑积分榜')
    .fontSize(24)
    .fontWeight(FontWeight.Bold)
    .margin({ top: 16 })
  
  if (this.isLoading) {
    LoadingProgress()
      .width(50)
      .height(50)
      .margin({ top: 32 })

else if (this.friends.length === 0) {

    Text('暂无好友数据')
      .fontSize(16)
      .margin({ top: 32 })

else {

    List({ space: 10 }) {
      ForEach(this.friends, (friend, index) => {
        ListItem() {
          FriendScoreItem({ friend, rank: index + 1 })

})

.layoutWeight(1)

    Text(我的排名: ${this.myRank})
      .fontSize(16)
      .margin({ top: 16 })

}

.padding(16)

private async loadLeaderboard(): Promise<void> {

this.isLoading = true;

try {
  const myPoints = await smileSyncService.getTotalPoints();
  const allFriends = await this.getFriendList();
  
  // 模拟从服务器获取排行榜数据
  this.friends = [

id: ‘friend1’, name: ‘张三’, avatar: ‘avatar1.png’, points: 125 },

id: ‘friend2’, name: ‘李四’, avatar: ‘avatar2.png’, points: 110 },

id: ‘friend3’, name: ‘王五’, avatar: ‘avatar3.png’, points: 95 },

id: ‘currentUser’, name: ‘我’, avatar: ‘my_avatar.png’, points: myPoints },

id: ‘friend4’, name: ‘赵六’, avatar: ‘avatar4.png’, points: 80 }

  ].sort((a, b) => b.points - a.points);
  
  this.myRank = this.friends.findIndex(f => f.id === 'currentUser') + 1;

catch (error) {

  console.error('加载排行榜失败:', error);

finally {

  this.isLoading = false;

}

private async getFriendList(): Promise<Friend[]> {
// 模拟获取好友列表
return new Promise((resolve) => {
setTimeout(() => {
resolve([
id: ‘friend1’, name: ‘张三’, avatar: ‘avatar1.png’ },

id: ‘friend2’, name: ‘李四’, avatar: ‘avatar2.png’ },

id: ‘friend3’, name: ‘王五’, avatar: ‘avatar3.png’ },

id: ‘friend4’, name: ‘赵六’, avatar: ‘avatar4.png’ }

    ]);
  }, 500);
});

}

@Component
struct FriendScoreItem {
private friend: FriendScore;
private rank: number;

build() {
Row() {
Text(this.rank.toString())
.fontSize(18)
.fontWeight(FontWeight.Bold)
.width(40)

  Image(this.friend.avatar)
    .width(40)
    .height(40)
    .borderRadius(20)
    .margin({ left: 8 })
  
  Column() {
    Text(this.friend.name)
      .fontSize(16)
      .fontColor(this.friend.id === 'currentUser' ? '#2196F3' : '#000000')
    
    Text(${this.friend.points}积分)
      .fontSize(14)
      .fontColor('#666666')
      .margin({ top: 4 })

.margin({ left: 8 })

  .layoutWeight(1)
  
  if (this.rank <= 3) {
    Image(resources/medal_${this.rank}.png)
      .width(30)
      .height(30)

}

.padding(12)

}

成就展示界面

// AchievementView.ets
@Component
struct AchievementView {
@State achievements: Achievement[] = [];
@State unlockedCount: number = 0;

aboutToAppear() {
this.loadAchievements();
EventBus.on(‘achievementUnlocked’, (achievements) => {
this.loadAchievements();
});
build() {

Column() {
  Text('微笑成就')
    .fontSize(24)
    .fontWeight(FontWeight.Bold)
    .margin({ top: 16 })
  
  Text(已解锁 {this.unlockedCount}/{this.achievements.length})
    .fontSize(16)
    .margin({ top: 8 })
  
  if (this.achievements.length === 0) {
    Text('加载中...')
      .fontSize(16)
      .margin({ top: 32 })

else {

    Grid() {
      ForEach(this.achievements, (achievement) => {
        GridItem() {
          AchievementItem({ achievement })

})

.columnsTemplate(‘1fr 1fr 1fr’)

    .rowsGap(20)
    .columnsGap(10)
    .margin({ top: 16 })
    .layoutWeight(1)

}

.padding(16)

private async loadAchievements(): Promise<void> {

this.achievements = await achievementService.getAllAchievements();
this.unlockedCount = this.achievements.filter(a => a.unlocked).length;

}

@Component
struct AchievementItem {
private achievement: Achievement;

build() {
Column() {
Image(this.achievement.unlocked ? this.achievement.icon : ‘resources/locked.png’)
.width(60)
.height(60)
.margin({ bottom: 8 })

  Text(this.achievement.name)
    .fontSize(14)
    .fontColor(this.achievement.unlocked ? '#000000' : '#999999')
    .textAlign(TextAlign.Center)
  
  if (this.achievement.unlocked) {
    Text(this.achievement.description)
      .fontSize(12)
      .margin({ top: 4 })

else {

    Text(未解锁)
      .fontSize(12)
      .margin({ top: 4 })

}

.padding(8)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.shadow({ radius: 4, color: '#00000020' })

}

四、高级功能实现
多设备微笑同步

// CollaborativeSmileService.ets
class CollaborativeSmileService {
private static instance: CollaborativeSmileService;

private constructor() {}

public static getInstance(): CollaborativeSmileService {
if (!CollaborativeSmileService.instance) {
CollaborativeSmileService.instance = new CollaborativeSmileService();
return CollaborativeSmileService.instance;

public async shareSmileToDevice(deviceId: string, smileRecord: SmileRecord): Promise<void> {

const ability = await featureAbility.startAbility({
  bundleName: 'com.example.smileDiary',
  abilityName: 'SmileSharingAbility',
  deviceId
});

await ability.call({
  method: 'receiveSmile',
  parameters: [smileRecord]
});

public async syncAllSmilesToNewDevice(deviceId: string): Promise<void> {

const records = await smileSyncService.getSmileRecords();
const ability = await featureAbility.startAbility({
  bundleName: 'com.example.smileDiary',
  abilityName: 'SmileSyncAbility',
  deviceId
});

await ability.call({
  method: 'receiveMultipleSmiles',
  parameters: [records]
});

public async challengeFriend(deviceId: string, challenge: SmileChallenge): Promise<void> {

const ability = await featureAbility.startAbility({
  bundleName: 'com.example.smileDiary',
  abilityName: 'SmileChallengeAbility',
  deviceId
});

await ability.call({
  method: 'receiveChallenge',
  parameters: [challenge]
});

}

export const collaborativeSmileService = CollaborativeSmileService.getInstance();

微笑挑战系统

// SmileChallengeService.ets
import reminderAgent from ‘@ohos.reminderAgent’;

class SmileChallengeService {
private static instance: SmileChallengeService;

private constructor() {}

public static getInstance(): SmileChallengeService {
if (!SmileChallengeService.instance) {
SmileChallengeService.instance = new SmileChallengeService();
return SmileChallengeService.instance;

public async createDailyChallenge(): Promise<void> {

const challenge: SmileChallenge = {
  id: generateId(),
  type: 'daily',
  target: 10, // 每天10次微笑
  reward: 50, // 50积分奖励
  deadline: this.getEndOfDay(),
  participants: ['currentUser']
};

await this.scheduleChallengeReminder(challenge);
EventBus.emit('newChallenge', challenge);

public async createFriendChallenge(friendId: string, target: number): Promise<void> {

const challenge: SmileChallenge = {
  id: generateId(),
  type: 'friend',
  target,
  reward: target * 2, // 双倍积分奖励
  deadline: this.getEndOfDay(),
  participants: ['currentUser', friendId]
};

await this.scheduleChallengeReminder(challenge);
EventBus.emit('newChallenge', challenge);

private getEndOfDay(): number {

const date = new Date();
date.setHours(23, 59, 59, 999);
return date.getTime();

private async scheduleChallengeReminder(challenge: SmileChallenge): Promise<void> {

const reminderRequest: reminderAgent.ReminderRequest = {
  reminderType: reminderAgent.ReminderType.REMINDER_TYPE_TIMER,
  actionButton: [{ title: '查看' }],
  wantAgent: {
    pkgName: 'com.example.smileDiary',
    abilityName: 'ChallengeReminderAbility'
  },
  triggerTime: challenge.deadline - 2  60  60 * 1000, // 提前2小时提醒
  title: '微笑挑战即将结束',
  content: 你还有2小时完成挑战: ${challenge.target}次微笑,
  expiredContent: "挑战已结束"
};

await reminderAgent.publishReminder(reminderRequest);

public async checkChallengeProgress(challengeId: string): Promise<ChallengeProgress> {

const challenge = await this.getChallenge(challengeId);
if (!challenge) throw new Error('Challenge not found');

const progress: ChallengeProgress = {
  challengeId,
  completed: false,
  current: 0,
  remaining: challenge.target,
  participants: []
};

// 获取参与者的微笑记录
for (const participant of challenge.participants) {
  const records = await smileSyncService.getSmileRecords();
  const today = new Date().toISOString().split('T')[0];
  const todaySmiles = records.filter(r => 
    r.userId = participant && r.date = today
  );
  
  progress.participants.push({
    userId: participant,
    count: todaySmiles.length
  });
  
  if (participant === 'currentUser') {
    progress.current = todaySmiles.length;
    progress.remaining = Math.max(0, challenge.target - progress.current);
    progress.completed = progress.current >= challenge.target;

}

return progress;

public async awardChallenge(challengeId: string): Promise<void> {

const challenge = await this.getChallenge(challengeId);
if (!challenge) throw new Error('Challenge not found');

const progress = await this.checkChallengeProgress(challengeId);
if (progress.completed) {
  pointService.addPoints(challenge.reward);
  
  EventBus.emit('challengeCompleted', {
    challengeId,
    reward: challenge.reward
  });

}

private async getChallenge(challengeId: string): Promise<SmileChallenge | null> {
// 简化实现,实际应从数据库获取
return Promise.resolve({
id: challengeId,
type: ‘daily’,
target: 10,
reward: 50,
deadline: this.getEndOfDay(),
participants: [‘currentUser’]
});
}

export const challengeService = SmileChallengeService.getInstance();

成就系统服务

// AchievementService.ets
class AchievementService {
private static instance: AchievementService;

private constructor() {}

public static getInstance(): AchievementService {
if (!AchievementService.instance) {
AchievementService.instance = new AchievementService();
return AchievementService.instance;

public getAllAchievements(): Promise<Achievement[]> {

// 模拟从数据库获取成就列表
return Promise.resolve([

id: ‘first_smile’,

    name: '初次微笑',
    description: '完成第一次微笑检测',
    icon: 'achievement1.png',
    unlocked: true,
    unlockDate: Date.now() - 86400000
  },

id: ‘smile_master’,

    name: '微笑大师',
    description: '累计微笑100次',
    icon: 'achievement2.png',
    unlocked: false,
    unlockDate: null
  },

id: ‘daily_champion’,

    name: '每日冠军',
    description: '一天内微笑20次',
    icon: 'achievement3.png',
    unlocked: false,
    unlockDate: null
  },

id: ‘happy_hour’,

    name: '快乐时光',
    description: '连续5天每天微笑10次',
    icon: 'achievement4.png',
    unlocked: false,
    unlockDate: null
  },

id: ‘social_butterfly’,

    name: '社交达人',
    description: '与5位好友分享微笑',
    icon: 'achievement5.png',
    unlocked: false,
    unlockDate: null
  },

id: ‘point_collector’,

    name: '积分收藏家',
    description: '累计获得500积分',
    icon: 'achievement6.png',
    unlocked: false,
    unlockDate: null

]);

public checkAchievements(totalPoints: number, dailyRecords: DailyRecord[]): Achievement[] {

const unlocked: Achievement[] = [];

// 检查积分成就
if (totalPoints >= 500) {
  unlocked.push(this.createUnlockedAchievement('point_collector'));

// 检查连续微笑成就

const consecutiveDays = this.checkConsecutiveDays(dailyRecords, 10, 5);
if (consecutiveDays >= 5) {
  unlocked.push(this.createUnlockedAchievement('happy_hour'));

// 检查单日微笑成就

const maxDailySmiles = Math.max(...dailyRecords.map(r => r.smiles.length));
if (maxDailySmiles >= 20) {
  unlocked.push(this.createUnlockedAchievement('daily_champion'));

return unlocked;

private checkConsecutiveDays(records: DailyRecord[], minSmiles: number, minDays: number): number {

let currentStreak = 0;
let maxStreak = 0;

// 按日期排序
const sortedRecords = [...records].sort((a, b) => 
  new Date(a.date).getTime() - new Date(b.date).getTime()
);

for (const record of sortedRecords) {
  if (record.smiles.length >= minSmiles) {
    currentStreak++;
    maxStreak = Math.max(maxStreak, currentStreak);

else {

    currentStreak = 0;

}

return maxStreak;

private createUnlockedAchievement(id: string): Achievement {

return {
  id,
  name: '',
  description: '',
  icon: '',
  unlocked: true,
  unlockDate: Date.now()
};

}

export const achievementService = AchievementService.getInstance();

五、总结

本微笑打卡应用实现了以下核心价值:
精准检测:利用AI技术准确识别用户微笑

正向激励:通过积分和成就系统鼓励用户保持微笑

社交互动:支持好友挑战和排行榜功能

多设备同步:跨设备同步微笑记录和成就

健康促进:通过微笑改善用户情绪和心理健康

扩展方向:
增加AR滤镜和拍照功能

开发团队微笑挑战模式

集成心理健康知识推送

增加微笑数据分析报告

注意事项:
需要申请ohos.permission.CAMERA权限

微笑检测准确率受光线和角度影响

多设备协同需保持网络连接

首次使用建议完成教程

部分高级功能可能需要订阅

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报
回复
    相关推荐