当前位置: 首页 > news >正文

openharmony之分布式相机开发:预览\拍照\编辑\同步\删除\分享教程

一、环境准备

  1. 硬件与系统要求
    • 设备: 至少两台搭载摄像头的 OpenHarmony 设备,例如 RK3568、RK3566、RK3588 开发板等。真机调试是分布式功能开发的必要条件。
    • 系统版本: OpenHarmony 5.0 Release (5.0.0.71) 或更高版本。不同版本间 API 可能有差异,请务必保持一致。
    • 网络: 所有设备必须连接到 同一个局域网 (LAN) 内,这是设备发现和通信的基础。
  2. 开发工具
    • IDE: DevEco Studio 5.0.3.910 或更高版本。
    • SDK: Full SDK (API Version 12+)。相机、分布式硬件等系统级能力需要 Full SDK 支持,Public SDK 不包含这些接口。

二、分布式设备认证与连接

分布式操作的第一步是让设备之间相互发现并建立信任关系。

  1. 权限声明
    module.json5 文件中,必须声明以下权限,否则应用会因权限不足而崩溃。
    {"module": {// ...其他配置"requestPermissions": [{"name": "ohos.permission.DISTRIBUTED_DATASYNC", // 分布式数据管理核心权限"reason": "$string:permission_reason_distributed_datasync","usedScene": {"abilities": ["EntryAbility"],"when": "inuse"}},{"name": "ohos.permission.CAMERA", // 使用相机"reason": "$string:permission_reason_camera","usedScene": {"abilities": ["EntryAbility"],"when": "inuse"}},{"name": "ohos.permission.READ_MEDIA", // 读取媒体文件"reason": "$string:permission_reason_read_media","usedScene": {"abilities": ["EntryAbility"],"when": "inuse"}},{"name": "ohos.permission.WRITE_MEDIA", // 写入媒体文件"reason": "$string:permission_reason_write_media","usedScene": {"abilities": ["EntryAbility"],"when": "inuse"}},{"name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE", // 监听设备状态变化"usedScene": {"abilities": ["EntryAbility"],"when": "inuse"}}]}
    }
    
  2. 设备发现与认证 (完整封装)
    我们创建一个 DeviceManager.ets 单例类来统一管理设备相关的逻辑。
    // common/DeviceManager.ets
    import deviceManager from '@ohos.distributedHardware.deviceManager';
    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    const TAG = 'DeviceManager';
    export interface DeviceInfo {deviceId: string;deviceName: string;
    }
    export class DeviceManagerInstance {private static instance: DeviceManagerInstance;private dmInstance: deviceManager.DeviceManager | null = null;private deviceList: DeviceInfo[] = [];private callback: ((devices: DeviceInfo[]) => void) | null = null;private constructor() {}public static getInstance(): DeviceManagerInstance {if (!DeviceManagerInstance.instance) {DeviceManagerInstance.instance = new DeviceManagerInstance();}return DeviceManagerInstance.instance;}// 初始化设备管理器public async init(bundleName: string): Promise<void> {if (this.dmInstance) {return;}try {this.dmInstance = await deviceManager.createDeviceManager(bundleName);hilog.info(0x0000, TAG, 'DeviceManager created successfully.');this.registerDeviceListCallback();} catch (err) {hilog.error(0x0000, TAG, `Failed to create DeviceManager. Code: ${err.code}, message: ${err.message}`);}}// 注册设备状态监听private registerDeviceListCallback(): void {if (!this.dmInstance) return;this.dmInstance.on('deviceStateChange', (data) => {hilog.info(0x0000, TAG, `Device state changed: ${JSON.stringify(data)}`);this.refreshDeviceList();});this.dmInstance.on('deviceFound', (data) => {hilog.info(0x0000, TAG, `Device found: ${JSON.stringify(data)}`);this.refreshDeviceList();});this.dmInstance.on('discoverFail', (data) => {hilog.error(0x0000, TAG, `Device discover failed: ${JSON.stringify(data)}`);});this.refreshDeviceList();}// 刷新设备列表private refreshDeviceList(): void {if (!this.dmInstance) return;try {const devices = this.dmInstance.getTrustedDeviceList();this.deviceList = devices.map(device => ({deviceId: device.networkId,deviceName: device.deviceName}));hilog.info(0x0000, TAG, `Current trusted devices: ${JSON.stringify(this.deviceList)}`);if (this.callback) {this.callback(this.deviceList);}} catch (err) {hilog.error(0x0000, TAG, `Failed to get trusted device list. Code: ${err.code}, message: ${err.message}`);}}// 开始设备发现public startDeviceDiscovery(): void {if (!this.dmInstance) {hilog.error(0x0000, TAG, 'DeviceManager not initialized.');return;}const extraInfo = {'targetPkgName': 'com.example.distributedcamera' // 替换为你的包名};this.dmInstance.startDeviceDiscovery(extraInfo);hilog.info(0x0000, TAG, 'Start device discovery.');}// 认证设备public async authenticateDevice(deviceId: string): Promise<void> {if (!this.dmInstance) return;try {const pinCode = '123456'; // 固定PIN码或动态生成await this.dmInstance.authenticateDevice(deviceId, pinCode);promptAction.showToast({ message: '认证请求已发送,请在对方设备上确认。' });} catch (err) {hilog.error(0x0000, TAG, `Authentication failed. Code: ${err.code}, message: ${err.message}`);promptAction.showToast({ message: '认证失败' });}}// 获取设备列表public getDeviceList(): DeviceInfo[] {return this.deviceList;}// 订阅设备列表变化public onDeviceListChange(callback: (devices: DeviceInfo[]) => void): void {this.callback = callback;}
    }
    

三、相机预览实现

这是相机的核心功能。在 OpenHarmony 5.0 中,必须使用 CaptureSession 来兼容分布式相机。

  1. 创建预览界面 (CameraPreview.ets)
    我们使用 XComponent 组件来承载相机预览流。
    // pages/CameraPreview.ets
    import camera from '@ohos.multimedia.camera';
    import image from '@ohos.multimedia.image';
    import hilog from '@ohos.hilog';
    import { DeviceManagerInstance, DeviceInfo } from '../common/DeviceManager';
    const TAG = 'CameraPreview';
    @Entry
    @Component
    struct CameraPreviewPage {@State surfaceId: string = '';@State isPreviewing: boolean = false;@State deviceList: DeviceInfo[] = [];@State selectedDeviceId: string = ''; // 要连接的远程设备ID,为空则使用本地相机private mXComponentController: XComponentController = new XComponentController();private cameraManager: camera.CameraManager | null = null;private cameraInput: camera.CameraInput | null = null;private captureSession: camera.CaptureSession | null = null;private previewOutput: camera.PreviewOutput | null = null;aboutToAppear(): void {// 初始化设备管理器DeviceManagerInstance.getInstance().init('com.example.distributedcamera').then(() => {DeviceManagerInstance.getInstance().onDeviceListChange((devices) => {this.deviceList = devices;});DeviceManagerInstance.getInstance().startDeviceDiscovery();});}aboutToDisappear(): void {this.releaseCamera();}// 初始化相机async initCamera(surfaceId: string) {this.releaseCamera(); // 先释放之前的会话try {this.cameraManager = camera.getCameraManager(getContext(this));// 获取相机设备,支持分布式const cameras = await this.cameraManager.getSupportedCameras(this.selectedDeviceId);if (cameras.length === 0) {hilog.error(0x0000, TAG, 'No camera found.');return;}this.cameraInput = this.cameraManager.createCameraInput(cameras[0]);await this.cameraInput.open();// 【关键】创建 CaptureSessionthis.captureSession = this.cameraManager.createCaptureSession();this.captureSession.beginConfig();// 添加输入this.captureSession.addInput(this.cameraInput);// 创建并添加预览输出const previewProfile = this.cameraManager.getSupportedPreviewOutputProfiles(cameras[0])[0];this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, surfaceId);this.captureSession.addOutput(this.previewOutput);await this.captureSession.commitConfig();await this.captureSession.start();this.isPreviewing = true;hilog.info(0x0000, TAG, 'Camera preview started successfully.');} catch (error) {hilog.error(0x0000, TAG, `Failed to init camera. Code: ${error.code}, message: ${error.message}`);}}// 释放相机资源releaseCamera() {if (this.isPreviewing && this.captureSession) {this.captureSession.stop();}if (this.captureSession) {this.captureSession.release();this.captureSession = null;}if (this.previewOutput) {this.previewOutput.release();this.previewOutput = null;}if (this.cameraInput) {this.cameraInput.close();this.cameraInput.release();this.cameraInput = null;}this.isPreviewing = false;hilog.info(0x0000, TAG, 'Camera resources released.');}build() {Column() {// 设备选择列表Row() {Text('选择相机设备:')Select(this.deviceList.map(d => d.deviceName)).selected(0).value('本地相机').font({ size: 16 }).onSelect((index: number) => {if (index === 0) {this.selectedDeviceId = '';} else {this.selectedDeviceId = this.deviceList[index - 1].deviceId;}hilog.info(0x0000, TAG, `Selected device: ${this.selectedDeviceId}`);})}.width('100%').justifyContent(FlexAlign.Start).padding(10)// 预览区域XComponent({type: 'surface',controller: this.mXComponentController}).onLoad(() => {this.surfaceId = this.mXComponentController.getXComponentSurfaceId();hilog.info(0x0000, TAG, `XComponent surfaceId: ${this.surfaceId}`);this.initCamera(this.surfaceId);}).width('100%').height('60%')// 控制按钮Row({ space: 20 }) {Button('拍照').onClick(() => {// 拍照逻辑将在下一节实现hilog.info(0x0000, TAG, 'Take photo button clicked.');})}}}
    }
    

四、拍照与保存

  1. 集成拍照功能
    CameraPreview.ets 中,我们添加拍照输出和拍照逻辑。
    // 在 CameraPreview.ets 组件中添加状态和成员变量
    @State photoUri: string = ''; // 用于保存拍照后的URI
    private photoOutput: camera.PhotoOutput | null = null;
    // 修改 initCamera 方法,添加 PhotoOutput
    async initCamera(surfaceId: string) {// ... (前面的代码保持不变)this.captureSession.beginConfig();this.captureSession.addInput(this.cameraInput);this.captureSession.addOutput(this.previewOutput);// 【新增】创建并添加拍照输出const photoProfile = this.cameraManager.getSupportedPhotoOutputProfiles(cameras[0])[0];this.photoOutput = this.cameraManager.createPhotoOutput(photoProfile);this.captureSession.addOutput(this.photoOutput);await this.captureSession.commitConfig();await this.captureSession.start();// ... (后面的代码保持不变)
    }
    // 修改 releaseCamera 方法,释放 PhotoOutput
    releaseCamera() {// ... (前面的代码保持不变)if (this.photoOutput) {this.photoOutput.release();this.photoOutput = null;}// ... (后面的代码保持不变)
    }
    // 【新增】拍照函数
    takePicture() {if (!this.photoOutput) {hilog.error(0x0000, TAG, 'PhotoOutput is not initialized.');return;}const photoSettings: camera.PhotoCaptureSetting = {quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,rotation: camera.ImageRotation.ROTATION_0};this.photoOutput.capture(photoSettings, (err, photo) => {if (err || !photo) {hilog.error(0x0000, TAG, `Failed to capture photo. Code: ${err.code}, message: ${err.message}`);return;}hilog.info(0x0000, TAG, 'Photo captured successfully.');// photo.main.uri 就是图片的URI,直接保存即可this.photoUri = photo.main.uri;hilog.info(0x0000, TAG, `Photo saved at: ${this.photoUri}`);// 这里可以触发一个Toast提示用户拍照成功promptAction.showToast({ message: '拍照成功!' });});
    }
    // 修改 Button 的 onClick 事件
    Button('拍照').onClick(() => {this.takePicture();})
    
  2. 保存到分布式文件系统
    photoOutput.capture 回调中返回的 photo.main.uri 已经是一个指向分布式文件路径的 URI。OpenHarmony 的相机模块会自动将照片保存到应用的分布式目录下 (/data/storage/el2/distributedfiles/)。

五、图像编辑(修图)

我们创建一个新的页面 ImageEditor.ets 来编辑图片。

  1. 创建编辑页面
    从相机页面跳转过来,并传入图片 URI。
    // pages/ImageEditor.ets
    import image from '@ohos.multimedia.image';
    import fs from '@ohos.file.fs';
    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    const TAG = 'ImageEditor';
    @Entry
    @Component
    struct ImageEditorPage {@State imagePixelMap: image.PixelMap | undefined = undefined;@State editHistory: image.PixelMap[] = []; // 简单的编辑历史private currentHistoryIndex: number = -1;private imageUri: string = (router.getParams() as Record<string, string>)['uri'] ?? '';aboutToAppear(): void {this.loadImage();}async loadImage() {try {const imageSource = image.createImageSource(this.imageUri);this.imagePixelMap = await imageSource.createPixelMap();this.editHistory.push(this.imagePixelMap!);this.currentHistoryIndex = 0;} catch (error) {hilog.error(0x0000, TAG, `Failed to load image. Code: ${error.code}, message: ${error.message}`);}}// 应用编辑操作并保存到历史applyEdit(editFunction: (pixelMap: image.PixelMap) => void) {if (this.currentHistoryIndex < this.editHistory.length - 1) {this.editHistory = this.editHistory.slice(0, this.currentHistoryIndex + 1);}const newPixelMap = this.editHistory[this.currentHistoryIndex].clone();editFunction(newPixelMap);this.editHistory.push(newPixelMap);this.currentHistoryIndex++;this.imagePixelMap = newPixelMap;}// 保存编辑后的图片async saveEditedImage() {if (!this.imagePixelMap) return;try {const packOpts: image.PackingOption = { format: "image/jpeg", quality: 98 };const imagePacker = image.createImagePacker();const buffer = await imagePacker.packing(this.imagePixelMap, packOpts);const newUri = `${this.imageUri.split('.')[0]}_edited_${Date.now()}.jpg`;const file = fs.openSync(newUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);fs.writeSync(file.fd, buffer);fs.closeSync(file);hilog.info(0x0000, TAG, `Edited image saved to: ${newUri}`);promptAction.showToast({ message: '图片已保存!' });router.back();} catch (error) {hilog.error(0x0000, TAG, `Failed to save edited image. Code: ${error.code}, message: ${error.message}`);}}build() {Column() {// 图片显示区域Image(this.imagePixelMap).objectFit(ImageFit.Contain).width('100%').height('50%')// 编辑按钮Row({ space: 10 }) {Button('裁剪').onClick(() => {this.applyEdit((pm) => {pm.crop({ x: 50, y: 50, size: { height: 200, width: 200 } });});})Button('旋转90°').onClick(() => {this.applyEdit((pm) => {pm.rotate(90);});})Button('灰度').onClick(() => {this.applyEdit((pm) => {// 读取像素数据,处理,再写回const area = { x: 0, y: 0, size: { height: pm.getHeight(), width: pm.getWidth() } };const buffer = new ArrayBuffer(pm.getBytesNumberPerRow() * pm.getHeight());pm.readPixels(buffer, area);// ... 在这里进行灰度算法处理 ...pm.writePixels(buffer, area);});})}.margin({ top: 20, bottom: 20 })Row({ space: 10 }) {Button('撤销').enabled(this.currentHistoryIndex > 0).onClick(() => {this.currentHistoryIndex--;this.imagePixelMap = this.editHistory[this.currentHistoryIndex];})Button('重做').enabled(this.currentHistoryIndex < this.editHistory.length - 1).onClick(() => {this.currentHistoryIndex++;this.imagePixelMap = this.editHistory[this.currentHistoryIndex];})Button('保存').onClick(() => {this.saveEditedImage();})}}.padding(10)}
    }
    
  2. 从相机页面跳转
    CameraPreview.ets 中,拍照成功后可以跳转到编辑页面。
    // 在 takePicture 函数的回调中
    this.photoUri = photo.main.uri;
    promptAction.showToast({ message: '拍照成功!' });
    router.pushUrl({url: 'pages/ImageEditor',params: { uri: this.photoUri }
    });
    

六、分布式图库同步与删除

如前文所述,仅依赖分布式文件系统扫描是不可靠的。我们采用 分布式文件系统 + 分布式数据库 (RDB) 的混合架构。

  1. 创建 RDB 辅助类 (RDBHelper.ets)
    // common/RDBHelper.ets
    import relationalStore from '@ohos.data.relationalStore';
    import hilog from '@ohos.hilog';
    const TAG = 'RDBHelper';
    const STORE_CONFIG = {name: 'DistributedGallery.db',securityLevel: relationalStore.SecurityLevel.S1
    };
    const CREATE_TABLE_SQL = 'CREATE TABLE IF NOT EXISTS IMAGES (ID INTEGER PRIMARY KEY AUTOINCREMENT, URI TEXT NOT NULL, DEVICE_ID TEXT NOT NULL, TIMESTAMP INTEGER)';
    export class RDBHelper {private static instance: RDBHelper;private store: relationalStore.RdbStore | null = null;private constructor() {}public static getInstance(): RDBHelper {if (!RDBHelper.instance) {RDBHelper.instance = new RDBHelper();}return RDBHelper.instance;}public async init(context: Context): Promise<void> {if (this.store) return;this.store = await relationalStore.getRdbStore(context, STORE_CONFIG);await this.store.executeSql(CREATE_TABLE_SQL);hilog.info(0x0000, TAG, 'RDB store initialized and table created.');}// 插入图片信息public async insertImage(uri: string, deviceId: string): Promise<void> {if (!this.store) return;const valueBucket = {'URI': uri,'DEVICE_ID': deviceId,'TIMESTAMP': Date.now()};await this.store.insert('IMAGES', valueBucket);hilog.info(0x0000, TAG, `Inserted image info: ${uri} from ${deviceId}`);}// 查询所有图片信息(分布式同步)public async queryAllImages(): Promise<Array<{uri: string, deviceId: string}>> {if (!this.store) return [];const predicates = new relationalStore.RdbPredicates('IMAGES');const result = await this.store.query(predicates, ['URI', 'DEVICE_ID']);const images: Array<{uri: string, deviceId: string}> = [];while (result.goToNextRow()) {images.push({uri: result.getString(0),deviceId: result.getString(1)});}result.close();return images;}// 删除图片信息public async deleteImage(uri: string): Promise<void> {if (!this.store) return;const predicates = new relationalStore.RdbPredicates('IMAGES');predicates.equalTo('URI', uri);await this.store.delete(predicates);hilog.info(0x0000, TAG, `Deleted image info: ${uri}`);}
    }
    
  2. 创建图库页面 (Gallery.ets)
    // pages/Gallery.ets
    import { RDBHelper } from '../common/RDBHelper';
    import fs from '@ohos.file.fs';
    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    const TAG = 'Gallery';
    @Entry
    @Component
    struct GalleryPage {@State imageList: Array<{uri: string, deviceId: string}> = [];@State refreshing: boolean = false;aboutToAppear(): void {this.loadImages();}async loadImages() {this.refreshing = true;try {// 从分布式RDB中获取图片元数据列表this.imageList = await RDBHelper.getInstance().queryAllImages();hilog.info(0x0000, TAG, `Loaded ${this.imageList.length} images from RDB.`);} catch (error) {hilog.error(0x0000, TAG, `Failed to load images. Code: ${error.code}, message: ${error.message}`);} finally {this.refreshing = false;}}async deleteImage(uri: string) {try {// 1. 从分布式文件系统删除文件fs.unlink(uri);// 2. 从分布式RDB删除记录await RDBHelper.getInstance().deleteImage(uri);promptAction.showToast({ message: '图片已删除' });this.loadImages(); // 刷新列表} catch (error) {hilog.error(0x0000, TAG, `Failed to delete image. Code: ${error.code}, message: ${error.message}`);promptAction.showToast({ message: '删除失败' });}}build() {Column() {Text('分布式图库').fontSize(24).fontWeight(FontWeight.Bold).margin(10)Refresh({ refreshing: $$refreshing }) {Grid() {ForEach(this.imageList, (item) => {GridItem() {Image(item.uri).objectFit(ImageFit.Cover).width('100%').height('100%').onClick(() => {// 点击可以跳转到编辑或预览页面router.pushUrl({ url: 'pages/ImageEditor', params: { uri: item.uri } });}).gesture(LongPressGesture().onAction(() => {// 长按弹出删除选项AlertDialog.show({title: '删除图片',message: '确定要删除这张图片吗?',primaryButton: {value: '取消',action: () => {}},secondaryButton: {value: '删除',fontColor: Color.Red,action: () => {this.deleteImage(item.uri);}}})}))}}, (item) => item.uri)}.columnsTemplate('1fr 1fr 1fr').columnsGap(8).rowsGap(8).padding(8)}.onRefreshing(() => {this.loadImages();})}}
    }
    
  3. 在拍照后同步到 RDB
    修改 CameraPreview.etstakePicture 函数,拍照成功后将信息存入 RDB。
    // 在 CameraPreview.ets 中
    import { RDBHelper } from '../common/RDBHelper';
    import deviceInfo from '@ohos.deviceInfo'; // 用于获取本机设备ID
    // ... 在 takePicture 的回调中
    this.photoUri = photo.main.uri;
    hilog.info(0x0000, TAG, `Photo saved at: ${this.photoUri}`);
    // 【新增】将图片信息同步到分布式RDB
    const localDeviceId = deviceInfo.networkId;
    await RDBHelper.getInstance().insertImage(this.photoUri, localDeviceId);
    promptAction.showToast({ message: '拍照成功!' });
    

七、分享图片

分享可以通过多种方式实现,这里我们使用分布式数据总线 (KV Store) 来发送一个临时的分享意图。

  1. 创建分享管理器 (ShareManager.ets)
    // common/ShareManager.ets
    import distributedData from '@ohos.data.distributedData';
    import hilog from '@ohos.hilog';
    const TAG = 'ShareManager';
    const KV_STORE_CONFIG = {userId: '0', // 用户ID,通常为'0'appId: 'com.example.distributedcamera', // 应用包名storeId: 'share_channel' // KV Store的唯一标识
    };
    export class ShareManager {private static instance: ShareManager;private kvManager: distributedData.KVManager | null = null;private kvStore: distributedData.KVStore | null = null;private constructor() {}public static getInstance(): ShareManager {if (!ShareManager.instance) {ShareManager.instance = new ShareManager();}return ShareManager.instance;}public async init(): Promise<void> {if (this.kvManager) return;this.kvManager = distributedData.createKVManager(KV_STORE_CONFIG);try {this.kvStore = await this.kvManager.getKVStore('default', { createIfMissing: true });hilog.info(0x0000, TAG, 'KV Store initialized for sharing.');} catch (e) {hilog.error(0x0000, TAG, `Failed to get KVStore. Code: ${e.code}, message: ${e.message}`);}}public async shareImage(uri: string): Promise<void> {if (!this.kvStore) {hilog.error(0x0000, TAG, 'KV Store is not initialized.');return;}const key = `share_${Date.now()}`;await this.kvStore.put(key, uri);hilog.info(0x0000, TAG, `Shared image with key: ${key}, uri: ${uri}`);}public subscribeToShares(callback: (uri: string) => void): void {if (!this.kvStore) return;this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => {if (data.insertEntries.length > 0) {const uri = data.insertEntries[0].value as string;hilog.info(0x0000, TAG, `Received shared image: ${uri}`);callback(uri);}});}
    }
    
  2. 在图库页面集成分享
    Gallery.ets 的长按菜单中添加分享按钮。
    // 在 Gallery.ets 的 LongPressGesture.onAction 中
    AlertDialog.show({title: '图片操作',message: '',buttons: [{text: '分享',color: Color.Blue,action: () => {ShareManager.getInstance().shareImage(item.uri);promptAction.showToast({ message: '分享成功!' });}},{text: '删除',color: Color.Red,action: () => {this.deleteImage(item.uri);}},{text: '取消',action: () => {}}]
    })
    
  3. 接收分享
    在应用启动时(如 UIAbilityonCreate 或主页面 aboutToAppear)初始化并订阅分享事件。
    // 在 EntryAbility.ets 或主页面
    import { ShareManager } from '../common/ShareManager';
    import router from '@ohos.router';
    // ...
    ShareManager.getInstance().init().then(() => {ShareManager.getInstance().subscribeToShares((uri: string) => {// 收到分享,可以弹窗提示用户,或直接跳转到图片预览/编辑页promptAction.showDialog({title: '收到一张分享的图片',message: `是否查看?\n${uri}`,buttons: [{ text: '查看', color: '#0099FF', action: () => {router.pushUrl({ url: 'pages/ImageEditor', params: { uri: uri } });}},{ text: '取消', color: '#666666', action: () => {} }]});});
    });
    // ...
    

八、完整流程与项目结构

  1. 应用启动流程
    • EntryAbility 启动,初始化 RDBHelperShareManager,并订阅分享事件。
    • 进入主页面(例如 Index.ets),提供进入相机和图库的入口。
  2. 相机流程
    • 进入 CameraPreview.ets,初始化 DeviceManager,发现并选择设备(本地或远程)。
    • 使用 CaptureSession 配置并启动预览。
    • 点击拍照,PhotoOutput 捕获图片,自动保存到分布式目录,并将 URI 和设备 ID 存入分布式 RDB。
    • 可选择跳转到 ImageEditor.ets 进行编辑,编辑后保存为新文件。
  3. 图库流程
    • 进入 Gallery.ets,从分布式 RDB 查询所有图片元数据列表并展示。
    • 点击图片可预览或编辑。
    • 长按图片可分享(通过 KV Store 发送 URI)或删除(同时删除文件和 RDB 记录)。
    • 其他设备上的应用会通过 KV Store 的订阅收到分享通知。
  4. 项目结构示例
    entry/src/main/
    ├── ets
    │   ├── entryability
    │   │   └── EntryAbility.ets
    │   ├── pages
    │   │   ├── Index.ets          // 主页
    │   │   ├── CameraPreview.ets  // 相机预览页
    │   │   ├── ImageEditor.ets    // 图片编辑页
    │   │   └── Gallery.ets        // 分布式图库页
    │   └── common
    │       ├── DeviceManager.ets  // 设备管理单例
    │       ├── RDBHelper.ets      // 分布式数据库辅助类
    │       └── ShareManager.ets   // 分享管理单例
    ├── module.json5                // 权限和模块配置
    └── resources
    

注意事项

  • 真机调试:分布式功能必须在真实设备上测试,模拟器无法模拟。
  • API 版本:本教程基于 API 12,请确保 DevEco Studio 和 SDK 版本匹配。
  • 错误处理:示例中的错误处理较为简单,生产级应用需要更健壮的异常捕获和用户提示。
  • 性能优化:图库加载大量图片时,应考虑分页加载、图片缓存(Image 组件的 cached 属性)等优化手段。
http://www.dtcms.com/a/498822.html

相关文章:

  • LeetCode 402 - 移掉 K 位数字
  • 皮卡丘XSS
  • 思维|栈
  • 关于网站建设方案的案例数码产品销售网站建设策划书
  • 2025年10月17日
  • Entity Framework Core和SqlSugar的区别,详细介绍
  • 【C语言】运算符
  • 网站备案帐号是什么菏泽微信小程序制作
  • 消息队列以及RabbitMQ的使用
  • PyCharm之服务器篇|Linux连接校园网Neu版
  • 在linux上训练深度学习环境配置(Ubuntu)
  • 洗车小程序系统
  • 网站 备案 营业执照太仓网站设计早晨设计
  • 煤矿网站建设WordPress高端主题 熊
  • 告别炼丹玄学:用元学习精准预测模型性能与数据需求,AWS AI Lab研究解读
  • 无需 VNC / 公网 IP!用 Docker-Webtop+cpolar,在手机浏览器远程操控 Linux
  • Vue3与Cesium:轻量版3D地理可视化实践
  • 数据预处理(音频/图像/视频/文字)及多模态统一大模型输入方案
  • 一段音频多段字幕,让音频能够流畅自然对应字幕 AI生成视频,扣子生成剪映视频草稿
  • Linux-网络安全私房菜(二)
  • 广州外贸网站建设 open需要做网站建设的公司
  • QML学习笔记(四十三)QML与C++交互:上下文属性暴露
  • Redis 的字符串底层实现
  • 递归-206.反转链表-力扣(LeetCode)
  • 【Linux系列】掌控 Linux 的脉搏:深入理解进程控制
  • 百度怎么注册公司网站wordpress 2019主题谷歌字体
  • 算法14.0
  • 基于掌纹生物特征的 Windows 安全登录实践:从物理安全到零信任身份验证
  • uniapp map地图添加浮层
  • 可靠的tcp连接