鸿蒙智能电子相册系统开发指南 原创

进修的泡芙
发布于 2025-6-23 13:24
浏览
0收藏

鸿蒙智能电子相册系统开发指南

一、系统架构设计

基于HarmonyOS的分布式能力和AI技术,我们设计了一套智能电子相册系统,主要功能包括:
图片懒加载:优化内存使用和加载速度

自适应背光:根据环境光线调整屏幕亮度

人脸识别翻页:通过人脸检测实现手势控制

多设备同步:跨终端同步相册内容和播放状态

智能分类:AI自动识别照片内容进行分类

!https://example.com/harmony-digital-frame-arch.png

二、核心代码实现
图片懒加载服务

// LazyLoadService.ets
import image from ‘@ohos.multimedia.image’;
import featureAbility from ‘@ohos.ability.featureAbility’;

class LazyLoadService {
private static instance: LazyLoadService;
private cache: Map<string, image.PixelMap> = new Map();
private loadingQueue: string[] = [];
private isProcessing: boolean = false;

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

// 预加载前几张图片
const preloadCount = 3;
const photos = photoService.getPhotos(0, preloadCount);
await Promise.all(photos.map(photo => this.loadImage(photo.url)));

public static getInstance(): LazyLoadService {

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

return LazyLoadService.instance;

public async getImage(url: string): Promise<image.PixelMap> {

// 如果已在缓存中,直接返回
if (this.cache.has(url)) {
  return this.cache.get(url)!;

// 加入加载队列

this.loadingQueue.push(url);

// 如果没有在处理队列,开始处理
if (!this.isProcessing) {
  this.processQueue();

// 返回占位图

return this.getPlaceholder();

private async processQueue(): Promise<void> {

this.isProcessing = true;

while (this.loadingQueue.length > 0) {
  const url = this.loadingQueue.shift()!;
  await this.loadImage(url);

this.isProcessing = false;

private async loadImage(url: string): Promise<void> {

try {
  // 使用FA能力加载图片
  const pixelMap = await featureAbility.loadImage(url);
  this.cache.set(url, pixelMap);
  
  // 通知图片已加载
  EventBus.emit('imageLoaded', url);

catch (error) {

  console.error(Failed to load image ${url}:, error);

}

private getPlaceholder(): image.PixelMap {
// 返回一个简单的占位图
return featureAbility.createPixelMap(100, 100, {
pixelFormat: image.PixelFormat.RGBA_8888,
alphaType: image.AlphaType.PREMUL
});
public clearCache(): void {

this.cache.clear();
this.loadingQueue = [];

public prefetchImages(urls: string[]): void {

urls.forEach(url => {
  if (!this.cache.has(url) && !this.loadingQueue.includes(url)) {
    this.loadingQueue.push(url);

});

if (!this.isProcessing) {
  this.processQueue();

}

export const lazyLoad = LazyLoadService.getInstance();

自适应背光服务

// BacklightService.ets
import sensor from ‘@ohos.sensor’;
import display from ‘@ohos.display’;
import power from ‘@ohos.power’;

class BacklightService {
private static instance: BacklightService;
private currentBrightness: number = 50;
private lightSensorData: sensor.LightResponse | null = null;
private powerMode: power.Mode = power.Mode.NORMAL;

private constructor() {
this.initLightSensor();
this.initPowerListener();
private initLightSensor(): void {

sensor.on(sensor.SensorType.SENSOR_TYPE_ID_LIGHT, (data) => {
  this.handleLightData(data);
}, { interval: 5000 }); // 5秒采样一次

private initPowerListener(): void {

power.on('powerModeChange', (mode) => {
  this.powerMode = mode;
  this.adjustBacklight();
});

public static getInstance(): BacklightService {

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

return BacklightService.instance;

private handleLightData(data: sensor.LightResponse): void {

this.lightSensorData = data;
this.adjustBacklight();

private adjustBacklight(): void {

if (!this.lightSensorData) return;

let targetBrightness: number;

// 根据环境光强度计算目标亮度
const lightLevel = this.lightSensorData.intensity;

// 不同电源模式下的亮度策略
switch (this.powerMode) {
  case power.Mode.POWER_SAVE:
    targetBrightness = this.calculateBrightness(lightLevel, 30, 70);
    break;
  case power.Mode.PERFORMANCE:
    targetBrightness = this.calculateBrightness(lightLevel, 50, 100);
    break;
  default:
    targetBrightness = this.calculateBrightness(lightLevel, 40, 90);

// 平滑过渡

const delta = targetBrightness - this.currentBrightness;
if (Math.abs(delta) > 5) {
  this.currentBrightness += delta * 0.2; // 20%的渐变
  display.setBrightness(this.currentBrightness);

}

private calculateBrightness(light: number, min: number, max: number): number {
// 简单的对数关系计算亮度
const normalized = Math.log1p(light) / 5; // 0-1范围
return min + normalized * (max - min);
public setManualBrightness(brightness: number): void {

this.currentBrightness = Math.max(10, Math.min(100, brightness));
display.setBrightness(this.currentBrightness);

public getCurrentBrightness(): number {

return this.currentBrightness;

public enableAutoBrightness(): void {

this.adjustBacklight();

public disableAutoBrightness(): void {

// 保持当前亮度

}

export const backlight = BacklightService.getInstance();

人脸识别翻页服务

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

class FaceDetectionService {
private static instance: FaceDetectionService;
private faceDetector: face.FaceDetector | null = null;
private cameraDevice: camera.CameraDevice | null = null;
private isDetecting: boolean = false;
private lastFaceTime: number = 0;

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

try {
  this.faceDetector = await face.createFaceDetector({
    modelPath: 'models/face_detection_lite.nn',
    performanceMode: face.PerformanceMode.FAST
  });

catch (error) {

  console.error('Failed to initialize face detector:', error);

}

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

public async startDetection(): Promise<void> {

if (this.isDetecting) return;

try {
  this.cameraDevice = await camera.getCameraDevice(camera.LensType.FRONT);
  await this.cameraDevice.setPreviewSize(640, 480);
  await this.cameraDevice.setFrameRate(15); // 15fps足够用于检测
  
  this.cameraDevice.on('frame', (frame) => {
    this.handleCameraFrame(frame);
  });
  
  await this.cameraDevice.startPreview();
  this.isDetecting = true;

catch (error) {

  console.error('Failed to start face detection:', error);

}

public async stopDetection(): void {
if (!this.isDetecting || !this.cameraDevice) return;

try {
  await this.cameraDevice.stopPreview();
  this.cameraDevice = null;
  this.isDetecting = false;

catch (error) {

  console.error('Failed to stop face detection:', error);

}

private async handleCameraFrame(frame: camera.CameraFrame): Promise<void> {
if (!this.faceDetector || Date.now() - this.lastFaceTime < 1000) return;

try {
  // 转换为PixelMap
  const pixelMap = await image.createPixelMapFromSurface(frame.surface);
  
  // 运行人脸检测
  const faces = await this.faceDetector.detect(pixelMap);
  
  if (faces.length > 0) {
    this.lastFaceTime = Date.now();
    this.handleFaceDetection(faces[0]);

} catch (error) {

  console.error('Face detection failed:', error);

}

private handleFaceDetection(face: face.FaceResult): void {
// 根据人脸位置判断翻页方向
const centerX = face.rect.left + face.rect.width / 2;
const screenCenter = display.getDefaultDisplay().width / 2;

if (centerX < screenCenter - 100) {
  // 人脸偏左,向右翻页
  EventBus.emit('pageTurn', 'right');

else if (centerX > screenCenter + 100) {

  // 人脸偏右,向左翻页
  EventBus.emit('pageTurn', 'left');

}

public isFaceDetected(): boolean {
return Date.now() - this.lastFaceTime < 2000; // 2秒内检测到人脸
}

export const faceDetection = FaceDetectionService.getInstance();

跨设备同步服务

// SyncService.ets
import distributedData from ‘@ohos.data.distributedData’;
import deviceManager from ‘@ohos.distributedHardware.deviceManager’;

class SyncService {
private static instance: SyncService;
private kvManager: distributedData.KVManager;
private kvStore: distributedData.KVStore;
private connectedDevices: string[] = [];

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

const config = {
  bundleName: 'com.example.digitalframe',
  userInfo: { userId: 'default' }
};

this.kvManager = distributedData.createKVManager(config);
this.kvStore = await this.kvManager.getKVStore('frame_sync', {
  createIfMissing: true,
  backup: false,
  autoSync: true,
  kvStoreType: distributedData.KVStoreType.SINGLE_VERSION
});

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

private initDeviceListener(): void {

deviceManager.on('deviceStateChange', (data) => {
  this.handleDeviceStateChange(data);
});

this.updateConnectedDevices();

private async updateConnectedDevices(): Promise<void> {

const devices = await deviceManager.getTrustedDeviceList();
this.connectedDevices = devices.map(d => d.deviceId);

private handleDeviceStateChange(data: deviceManager.DeviceStateChangeData): void {

if (data.deviceState === deviceManager.DeviceState.ONLINE) {
  if (!this.connectedDevices.includes(data.deviceId)) {
    this.connectedDevices.push(data.deviceId);

} else if (data.deviceState === deviceManager.DeviceState.OFFLINE) {

  this.connectedDevices = this.connectedDevices.filter(id => id !== data.deviceId);

}

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

public async syncAlbum(album: Album): Promise<void> {

const syncData: SyncAlbum = {
  ...album,
  deviceId: deviceManager.getLocalDevice().id,
  timestamp: Date.now()
};

await this.kvStore.put(album_${album.id}, JSON.stringify(syncData));

public async syncCurrentPhoto(photo: Photo): Promise<void> {

const syncData: SyncPhoto = {
  ...photo,
  deviceId: deviceManager.getLocalDevice().id,
  timestamp: Date.now()
};

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

public async syncPlayState(playing: boolean): Promise<void> {

const syncData: SyncPlayState = {
  playing,
  deviceId: deviceManager.getLocalDevice().id,
  timestamp: Date.now()
};

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

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

if (data.deviceId === deviceManager.getLocalDevice().id) return;

try {
  const parsed = JSON.parse(data.value);
  
  if (data.key.startsWith('album_')) {
    EventBus.emit('remoteAlbumUpdate', parsed);

else if (data.key === ‘current_photo’) {

    EventBus.emit('remotePhotoChange', parsed);

else if (data.key === ‘play_state’) {

    EventBus.emit('remotePlayState', parsed);

} catch (error) {

  console.error('Failed to parse sync data:', error);

}

public async getConnectedDevices(): Promise<string[]> {
await this.updateConnectedDevices();
return […this.connectedDevices];
public async broadcastCommand(command: FrameCommand): Promise<void> {

const syncData: SyncCommand = {
  type: 'command',
  command,
  timestamp: Date.now(),
  deviceId: deviceManager.getLocalDevice().id
};

await this.kvStore.put(command_${Date.now()}, JSON.stringify(syncData));

}

export const syncService = SyncService.getInstance();

三、主界面实现
相册主界面

// PhotoFrameView.ets
@Component
struct PhotoFrameView {
@State currentPhoto: Photo | null = null;
@State album: Album | null = null;
@State isPlaying: boolean = false;
@State playInterval: number = 5000; // 5秒切换
@State brightness: number = 50;
@State faceDetectionEnabled: boolean = true;
@State connectedDevices: number = 0;

private playTimer: number | null = null;

aboutToAppear() {
this.initEventListeners();
this.loadInitialData();
build() {

Stack() {
  // 照片显示
  if (this.currentPhoto) {
    PhotoView({
      photo: this.currentPhoto,
      onSwipeLeft: () => this.nextPhoto(),
      onSwipeRight: () => this.prevPhoto()
    })

else {

    LoadingView()

// 控制栏

  Position() {
    ControlBar({
      isPlaying: this.isPlaying,
      brightness: this.brightness,
      faceDetection: this.faceDetectionEnabled,
      onPlayPause: () => this.togglePlay(),
      onBrightnessChange: (value) => this.changeBrightness(value),
      onFaceDetectionToggle: (enabled) => this.toggleFaceDetection(enabled),
      onPrev: () => this.prevPhoto(),
      onNext: () => this.nextPhoto()
    })
    .position({ x: '50%', y: '90%' })
    .translate({ x: -150, y: -80 })

// 设备连接状态

  if (this.connectedDevices > 0) {
    Text(${this.connectedDevices}设备连接中)
      .fontSize(14)
      .fontColor('#FFFFFF')
      .position({ x: '90%', y: '5%' })

}

.width('100%')
.height('100%')

private initEventListeners(): void {

EventBus.on('remotePhotoChange', (photo) => {
  this.currentPhoto = photo;
});

EventBus.on('remotePlayState', (state) => {
  this.isPlaying = state.playing;
  this.updatePlayTimer();
});

EventBus.on('remoteAlbumUpdate', (album) => {
  this.album = album;
});

EventBus.on('pageTurn', (direction) => {
  if (direction === 'left') {
    this.prevPhoto();

else {

    this.nextPhoto();

});

EventBus.on('imageLoaded', (url) => {
  if (this.currentPhoto && this.currentPhoto.url === url) {
    this.currentPhoto = { ...this.currentPhoto }; // 触发重新渲染

});

private async loadInitialData(): Promise<void> {

this.album = await photoService.getDefaultAlbum();
this.currentPhoto = await photoService.getCurrentPhoto();
this.connectedDevices = (await syncService.getConnectedDevices()).length;
this.brightness = backlight.getCurrentBrightness();

// 同步初始状态
if (this.album) {
  syncService.syncAlbum(this.album);

if (this.currentPhoto) {

  syncService.syncCurrentPhoto(this.currentPhoto);

syncService.syncPlayState(this.isPlaying);

private async nextPhoto(): Promise<void> {

if (!this.album) return;

this.currentPhoto = await photoService.getNextPhoto();
syncService.syncCurrentPhoto(this.currentPhoto);

private async prevPhoto(): Promise<void> {

if (!this.album) return;

this.currentPhoto = await photoService.getPrevPhoto();
syncService.syncCurrentPhoto(this.currentPhoto);

private togglePlay(): void {

this.isPlaying = !this.isPlaying;
this.updatePlayTimer();
syncService.syncPlayState(this.isPlaying);

private updatePlayTimer(): void {

if (this.playTimer) {
  clearInterval(this.playTimer);
  this.playTimer = null;

if (this.isPlaying) {

  this.playTimer = setInterval(() => {
    this.nextPhoto();
  }, this.playInterval);

}

private changeBrightness(value: number): void {
this.brightness = value;
backlight.setManualBrightness(value);
private toggleFaceDetection(enabled: boolean): void {

this.faceDetectionEnabled = enabled;
if (enabled) {
  faceDetection.startDetection();

else {

  faceDetection.stopDetection();

}

aboutToDisappear() {
if (this.playTimer) {
clearInterval(this.playTimer);
faceDetection.stopDetection();

}

@Component
struct PhotoView {
private photo: Photo;
private onSwipeLeft: () => void;
private onSwipeRight: () => void;

build() {
Stack() {
// 使用懒加载服务获取图片
LazyImage({ url: this.photo.url })

  // 照片信息(可选)
  Position() {
    Text(this.photo.name || '')
      .fontSize(16)
      .fontColor('#FFFFFF')
      .backgroundColor('#66000000')
      .padding(8)
      .borderRadius(4)

.position({ x: ‘50%’, y: ‘85%’ })

.width(‘100%’)

.height('100%')
.gesture(
  GestureGroup(
    GestureMode.Sequence,
    PanGesture({ direction: PanDirection.Horizontal })
      .onActionEnd((event) => {
        if (event.offsetX > 50) {
          this.onSwipeRight();

else if (event.offsetX < -50) {

          this.onSwipeLeft();

})

  )
)

}

@Component
struct LazyImage {
private url: string;
@State pixelMap: image.PixelMap | null = null;

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

if (this.pixelMap) {
  Image(this.pixelMap)
    .objectFit(ImageFit.Contain)
    .width('100%')
    .height('100%')

else {

  LoadingProgress()
    .width(50)
    .height(50)

}

private async loadImage(): Promise<void> {
this.pixelMap = await lazyLoad.getImage(this.url);
}

@Component
struct ControlBar {
private isPlaying: boolean;
private brightness: number;
private faceDetection: boolean;
private onPlayPause: () => void;
private onBrightnessChange: (value: number) => void;
private onFaceDetectionToggle: (enabled: boolean) => void;
private onPrev: () => void;
private onNext: () => void;

build() {
Row() {
// 上一张按钮
Button(‘上一张’)
.onClick(() => this.onPrev())
.width(80)

  // 播放/暂停按钮
  Button(this.isPlaying ? '暂停' : '播放')
    .onClick(() => this.onPlayPause())
    .width(80)
    .margin({ left: 8, right: 8 })
  
  // 下一张按钮
  Button('下一张')
    .onClick(() => this.onNext())
    .width(80)
  
  // 亮度滑块
  Slider({
    value: this.brightness,
    min: 10,
    max: 100,
    step: 5
  })
  .onChange((value) => this.onBrightnessChange(value))
  .width(150)
  .margin({ left: 16 })
  
  // 人脸识别开关
  Toggle({ type: ToggleType.Switch, isOn: this.faceDetection })
    .onChange((isOn) => this.onFaceDetectionToggle(isOn))
    .margin({ left: 16 })

.padding(12)

.backgroundColor('#66000000')
.borderRadius(20)

}

相册管理界面

// AlbumManagementView.ets
@Component
struct AlbumManagementView {
@State albums: Album[] = [];
@State selectedAlbum: Album | null = null;

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

Row() {
  // 相册列表
  AlbumList({
    albums: this.albums,
    selectedId: this.selectedAlbum?.id,
    onSelect: (album) => this.selectAlbum(album)
  })
  .width('30%')
  
  // 相册详情
  if (this.selectedAlbum) {
    AlbumDetail({ album: this.selectedAlbum })
      .width('70%')

}

.height('100%')

private async loadAlbums(): Promise<void> {

this.albums = await photoService.getAllAlbums();
if (this.albums.length > 0) {
  this.selectedAlbum = this.albums[0];

}

private selectAlbum(album: Album): void {
this.selectedAlbum = album;
photoService.setCurrentAlbum(album.id);
syncService.syncAlbum(album);
}

@Component
struct AlbumList {
private albums: Album[];
private selectedId: string | null;
private onSelect: (album: Album) => void;

build() {
List() {
ForEach(this.albums, (album) => {
ListItem() {
AlbumItem({
album,
selected: album.id === this.selectedId,
onClick: () => this.onSelect(album)
})
})

.divider({ strokeWidth: 1, color: ‘#F0F0F0’ })

}

@Component
struct AlbumItem {
private album: Album;
private selected: boolean;
private onClick: () => void;

build() {
Row() {
Image(this.album.coverUrl || ‘resources/base/media/ic_default_album.png’)
.width(60)
.height(60)
.objectFit(ImageFit.Cover)
.margin({ right: 12 })

  Column() {
    Text(this.album.name)
      .fontSize(18)
      .fontWeight(FontWeight.Bold)
    
    Text(${this.album.count}张照片)
      .fontSize(14)
      .fontColor('#666666')

.layoutWeight(1)

.padding(12)

.backgroundColor(this.selected ? '#E3F2FD' : '#FFFFFF')
.onClick(() => this.onClick())

}

@Component
struct AlbumDetail {
private album: Album;

build() {
Column() {
// 相册封面
Image(this.album.coverUrl || ‘resources/base/media/ic_default_album.png’)
.width(‘100%’)
.height(200)
.objectFit(ImageFit.Cover)

  // 照片网格
  PhotoGrid({ photos: this.album.photos })
    .margin({ top: 16 })

.padding(16)

}

@Component
struct PhotoGrid {
private photos: Photo[];

build() {
Grid() {
ForEach(this.photos, (photo) => {
GridItem() {
PhotoThumbnail({ photo })
})

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

.rowsGap(12)
.columnsGap(12)

}

@Component
struct PhotoThumbnail {
private photo: Photo;
@State pixelMap: image.PixelMap | null = null;

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

if (this.pixelMap) {
  Image(this.pixelMap)
    .width('100%')
    .aspectRatio(1)
    .objectFit(ImageFit.Cover)
    .borderRadius(4)

else {

  LoadingProgress()
    .width(30)
    .height(30)

}

private async loadImage(): Promise<void> {
this.pixelMap = await lazyLoad.getImage(this.photo.thumbnailUrl);
}

四、高级功能实现
照片分类服务

// PhotoClassificationService.ets
import ai from ‘@ohos.ai’;
import image from ‘@ohos.multimedia.image’;

class PhotoClassificationService {
private static instance: PhotoClassificationService;
private classifier: ai.ImageClassifier | null = null;

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

try {
  this.classifier = await ai.createImageClassifier({
    modelPath: 'models/image_classification_lite.nn',
    labelPath: 'models/classification_labels.json'
  });

catch (error) {

  console.error('Failed to initialize classifier:', error);

}

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

public async classifyPhoto(photo: Photo): Promise<PhotoCategory[]> {

if (!this.classifier) return [];

try {
  const pixelMap = await lazyLoad.getImage(photo.url);
  const results = await this.classifier.classify(pixelMap);
  
  return results.map(result => ({
    label: result.label,
    confidence: result.confidence
  }));

catch (error) {

  console.error('Photo classification failed:', error);
  return [];

}

public async autoOrganizeAlbums(): Promise<void> {
const photos = await photoService.getAllPhotos();
const categories = new Map<string, Photo[]>();

// 分类所有照片
for (const photo of photos) {
  const categories = await this.classifyPhoto(photo);
  if (categories.length > 0) {
    const primaryCategory = categories[0].label;
    
    if (!categories.has(primaryCategory)) {
      categories.set(primaryCategory, []);

categories.get(primaryCategory)!.push(photo);

}

// 创建或更新相册
for (const [category, photos] of categories.entries()) {
  await photoService.createOrUpdateAlbum({
    name: category,
    coverUrl: photos[0].url,
    photos
  });

}

public async getSuggestedAlbums(): Promise<Album[]> {
const photos = await photoService.getUncategorizedPhotos();
const suggestions: Album[] = [];

// 对未分类照片进行分组
const groups = new Map<string, Photo[]>();
for (const photo of photos) {
  const categories = await this.classifyPhoto(photo);
  if (categories.length > 0) {
    const label = categories[0].label;
    
    if (!groups.has(label)) {
      groups.set(label, []);

groups.get(label)!.push(photo);

}

// 生成建议相册
for (const [label, photos] of groups.entries()) {
  if (photos.length >= 5) { // 至少5张照片才建议创建相册
    suggestions.push({
      id: generateId(),
      name: label,
      coverUrl: photos[0].url,
      count: photos.length,
      photos
    });

}

return suggestions;

}

export const photoClassifier = PhotoClassificationService.getInstance();

播放管理服务

// PlaybackService.ets
import { photoService } from ‘./PhotoService’;
import { syncService } from ‘./SyncService’;

class PlaybackService {
private static instance: PlaybackService;
private playTimer: number | null = null;
private currentInterval: number = 5000; // 默认5秒
private isPlaying: boolean = false;
private playMode: PlayMode = ‘sequential’;

private constructor() {
this.initEventListeners();
private initEventListeners(): void {

EventBus.on('remotePlayCommand', (command) => {
  this.handleRemoteCommand(command);
});

public static getInstance(): PlaybackService {

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

return PlaybackService.instance;

public startPlayback(): void {

if (this.isPlaying) return;

this.isPlaying = true;
this.startTimer();
syncService.syncPlayState(true);

public stopPlayback(): void {

if (!this.isPlaying) return;

this.isPlaying = false;
this.stopTimer();
syncService.syncPlayState(false);

public togglePlayback(): void {

if (this.isPlaying) {
  this.stopPlayback();

else {

  this.startPlayback();

}

public setInterval(interval: number): void {
this.currentInterval = interval;

if (this.isPlaying) {
  this.restartTimer();

}

public setPlayMode(mode: PlayMode): void {
this.playMode = mode;
private startTimer(): void {

this.playTimer = setInterval(() => {
  this.nextPhoto();
}, this.currentInterval);

private stopTimer(): void {

if (this.playTimer) {
  clearInterval(this.playTimer);
  this.playTimer = null;

}

private restartTimer(): void {
this.stopTimer();
this.startTimer();
private async nextPhoto(): Promise<void> {

let nextPhoto: Photo;

switch (this.playMode) {
  case 'random':
    nextPhoto = await photoService.getRandomPhoto();
    break;
  case 'shuffle':
    nextPhoto = await photoService.getNextShuffledPhoto();
    break;
  default:
    nextPhoto = await photoService.getNextPhoto();

if (nextPhoto) {

  photoService.setCurrentPhoto(nextPhoto);
  syncService.syncCurrentPhoto(nextPhoto);

}

private handleRemoteCommand(command: PlayCommand): void {
switch (command) {
case ‘play’:
this.startPlayback();
break;
case ‘pause’:
this.stopPlayback();
break;
case ‘next’:
this.nextPhoto();
break;
case ‘prev’:
this.prevPhoto();
break;
}

private async prevPhoto(): Promise<void> {
const prevPhoto = await photoService.getPrevPhoto();
if (prevPhoto) {
photoService.setCurrentPhoto(prevPhoto);
syncService.syncCurrentPhoto(prevPhoto);
}

public isPlaying(): boolean {
return this.isPlaying;
public getCurrentInterval(): number {

return this.currentInterval;

public getPlayMode(): PlayMode {

return this.playMode;

}

export const playback = PlaybackService.getInstance();

照片管理服务

// PhotoService.ets
import storage from ‘@ohos.data.storage’;

class PhotoService {
private static instance: PhotoService;
private albums: Album[] = [];
private currentAlbumId: string | null = null;
private currentPhotoId: string | null = null;
private shuffleOrder: string[] = [];

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

const albumsData = await storage.get('photoAlbums');
if (albumsData) {
  this.albums = JSON.parse(albumsData);

else {

  // 初始化默认相册
  this.albums = [{
    id: 'default',
    name: '所有照片',
    coverUrl: '',
    count: 0,
    photos: []
  }];

}

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

public async getAllAlbums(): Promise<Album[]> {

return [...this.albums];

public async getAlbum(id: string): Promise<Album | null> {

return this.albums.find(album => album.id === id) || null;

public async getCurrentAlbum(): Promise<Album | null> {

if (!this.currentAlbumId) return null;
return this.getAlbum(this.currentAlbumId);

public async setCurrentAlbum(id: string): Promise<void> {

this.currentAlbumId = id;
this.shuffleOrder = [];
await storage.set('currentAlbum', id);

public async getCurrentPhoto(): Promise<Photo | null> {

if (!this.currentPhotoId) {
  const album = await this.getCurrentAlbum();
  if (album && album.photos.length > 0) {
    this.currentPhotoId = album.photos[0].id;

}

if (!this.currentPhotoId) return null;

return this.getPhoto(this.currentPhotoId);

public async setCurrentPhoto(photo: Photo): Promise<void> {

this.currentPhotoId = photo.id;
await storage.set('currentPhoto', photo.id);

public async getNextPhoto(): Promise<Photo | null> {

const album = await this.getCurrentAlbum();
if (!album || album.photos.length === 0) return null;

const currentIndex = album.photos.findIndex(p => p.id === this.currentPhotoId);
if (currentIndex === -1) return album.photos[0];

const nextIndex = (currentIndex + 1) % album.photos.length;
return album.photos[nextIndex];

public async getPrevPhoto(): Promise<Photo | null> {

const album = await this.getCurrentAlbum();
if (!album || album.photos.length === 0) return null;

const currentIndex = album.photos.findIndex(p => p.id === this.currentPhotoId);
if (currentIndex === -1) return album.photos[0];

const prevIndex = (currentIndex - 1 + album.photos.length) % album.photos.length;
return album.photos[prevIndex];

public async getRandomPhoto(): Promise<Photo | null> {

const album = await this.getCurrentAlbum();
if (!album || album.photos.length === 0) return null;

const randomIndex = Math.floor(Math.random() * album.photos.length);
return album.photos[randomIndex];

public async getNextShuffledPhoto(): Promise<Photo | null> {

const album = await this.getCurrentAlbum();
if (!album || album.photos.length === 0) return null;

// 初始化或重新生成随机顺序
if (this.shuffleOrder.length === 0 || 
    this.shuffleOrder.length !== album.photos.length) {
  this.generateShuffleOrder(album.photos);

// 获取当前照片在随机顺序中的位置

let currentPos = 0;
if (this.currentPhotoId) {
  currentPos = this.shuffleOrder.indexOf(this.currentPhotoId);
  if (currentPos === -1) currentPos = 0;

// 获取下一张照片

const nextPos = (currentPos + 1) % this.shuffleOrder.length;
const nextPhotoId = this.shuffleOrder[nextPos];
return this.getPhoto(nextPhotoId);

private generateShuffleOrder(photos: Photo[]): void {

this.shuffleOrder = photos.map(p => p.id);

// Fisher-Yates洗牌算法
for (let i = this.shuffleOrder.length - 1; i > 0; i--) {
  const j = Math.floor(Math.random() * (i + 1));
  [this.shuffleOrder[i], this.shuffleOrder[j]] = 
    [this.shuffleOrder[j], this.shuffleOrder[i]];

}

private async getPhoto(id: string): Promise<Photo | null> {
for (const album of this.albums) {
const photo = album.photos.find(p => p.id === id);
if (photo) return photo;
return null;

public async getAllPhotos(): Promise<Photo[]> {

return this.albums.flatMap(album => album.photos);

public async getUncategorizedPhotos(): Promise<Photo[]> {

const allPhotos = await this.getAllPhotos();
return allPhotos.filter(photo => 
  !this.albums.some(album => 
    album.id !== 'default' && 
    album.photos.some(p => p.id === photo.id)
  )
);

public async createOrUpdateAlbum(album: Album): Promise<void> {

const existingIndex = this.albums.findIndex(a => a.id === album.id);

if (existingIndex >= 0) {
  this.albums[existingIndex] = album;

else {

  this.albums.push(album);

await this.saveAlbums();

public async addPhotosToAlbum(albumId: string, photos: Photo[]): Promise<void> {

const album = this.albums.find(a => a.id === albumId);
if (!album) return;

// 去重
const existingIds = new Set(album.photos.map(p => p.id));
const newPhotos = photos.filter(p => !existingIds.has(p.id));

album.photos.push(...newPhotos);
album.count = album.photos.length;

if (album.photos.length > 0 && !album.coverUrl) {
  album.coverUrl = album.photos[0].url;

await this.saveAlbums();

private async saveAlbums(): Promise<void> {

await storage.set('photoAlbums', JSON.stringify(this.albums));

}

export const photoService = PhotoService.getInstance();

五、总结

本智能电子相册系统实现了以下核心价值:
高效图片管理:懒加载技术优化内存使用和加载速度

智能环境适应:自动调节背光保护眼睛并节省电量

自然交互体验:人脸识别实现无接触翻页控制

多设备同步:跨终端共享相册内容和播放状态

智能分类:AI自动识别照片内容并分类整理

扩展方向:
增加云相册同步功能

开发照片编辑和美化工具

添加音乐播放功能创建幻灯片秀

支持语音控制命令

集成社交媒体分享功能

注意事项:
人脸识别功能需在良好光照条件下使用

懒加载服务会占用部分内存缓存图片

跨设备同步需要设备登录相同账号

首次使用建议进行相册整理

自动分类功能需要一定数量的照片才能准确工作

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