
鸿蒙智能电子相册系统开发指南 原创
鸿蒙智能电子相册系统开发指南
一、系统架构设计
基于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自动识别照片内容并分类整理
扩展方向:
增加云相册同步功能
开发照片编辑和美化工具
添加音乐播放功能创建幻灯片秀
支持语音控制命令
集成社交媒体分享功能
注意事项:
人脸识别功能需在良好光照条件下使用
懒加载服务会占用部分内存缓存图片
跨设备同步需要设备登录相同账号
首次使用建议进行相册整理
自动分类功能需要一定数量的照片才能准确工作
