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

37 相册读取与图片选择:PhotoViewPicker 实战

37 相册读取与图片选择:PhotoViewPicker 实战

前言

图:37 相册读取与图片选择:PhotoViewPicker 实战 运行效果截图(HarmonyOS NEXT)

除了直接拍照,从相册选择已有图片也是常见的图片获取方式。鸿蒙提供了PhotoViewPicker——一个系统级的图片选择器,让用户可以从相册中选择一张或多张图片。

本文以"鹿鹿·笔迹心理分析"项目中 [CapturePage.ets](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/pages/CapturePage.ets) 的pickFromAlbum()方法为例,深入解析 PhotoViewPicker 的使用方法和相册选择流程。

鸿蒙官方·PhotoViewPicker 文档:developer.huawei.com
项目源码仓库:harmony-app GitHub

图:PhotoViewPicker 相册选择流程——创建选择器 → 配置选项 → 获取 URI

文件系统系统相册 UIPhotoViewPickerCapturePage文件系统系统相册 UIPhotoViewPickerCapturePagenew photoAccessHelper.PhotoViewPicker()select(PhotoSelectOptions)打开系统相册界面用户浏览并选择图片返回选中图片PhotoSelectResult { photoUris }通过 URI 读取图片数据

一、PhotoViewPicker 基础

1.1 模块导入

import{photoAccessHelper}from'@kit.MediaLibraryKit'

1.2 基本使用

privateasyncpickFromAlbum(){try{// ① 创建选择器constphotoPicker=newphotoAccessHelper.PhotoViewPicker()// ② 配置选项constpickerOptions:photoAccessHelper.PhotoSelectOptions={maxSelectNumber:1// 最多选择 1 张}// ③ 打开选择器constresult:photoAccessHelper.PhotoSelectResult=awaitphotoPicker.select(pickerOptions)// ④ 处理结果if(result&&result.photoUris&&result.photoUris.length>0){this.previewPath=result.photoUris[0]this.hasPreview=truethis.source='gallery'}}catch(error){hilog.error(0x0000,TAG,'相册选择失败: %s',JSON.stringify(error))}}

二、PhotoSelectOptions 配置

2.1 选项详解

interfacePhotoSelectOptions{maxSelectNumber:number// 最大选择数量MIME?:string[]// 文件类型过滤isEdit?:boolean// 是否允许编辑}

2.2 实际配置

参数本项目值说明
maxSelectNumber1一次分析一张笔迹图片
MIME未指定默认所有图片类型
isEdit未指定选择后不编辑

2.3 扩展配置示例

// 多选 + MIME 过滤constpickerOptions:photoAccessHelper.PhotoSelectOptions={maxSelectNumber:9,MIME:['image/jpeg','image/png'],}

三、PhotoSelectResult 结果处理

3.1 返回结构

interfacePhotoSelectResult{photoUris:string[]// 所选图片的 URI 列表}

3.2 URI 的使用

result.photoUris[0]// 返回类似:content://media/external/images/media/12345// 或:/data/storage/el2/base/files/...// 这个 URI 可以直接用于:// 1. image.createImageSource(uri) — 解码图像// 2. Image(uri) — ArkUI 图片组件展示// 3. fs.openSync(uri) — 文件操作

四、拍照/相册双模式切换

4.1 模式切换策略

CapturePage 支持两种来源——拍照和相册,通过source参数控制:

aboutToAppear(){// 从路由参数读取来源constparams=this.getUIContext().getRouter().getParams()asRecord<string,Object>if(params&&typeofparams['source']==='string'){this.source=params['source']asstring}if(this.source==='gallery'){this.pickFromAlbum()// → 相册模式}else{this.initCamera()// → 相机模式}}

4.2 触发来源

// 跳转到 CapturePage — 拍照模式this.getUIContext().getRouter().pushUrl({url:'pages/CapturePage',params:{source:'camera'}})// 跳转到 CapturePage — 相册模式this.getUIContext().getRouter().pushUrl({url:'pages/CapturePage',params:{source:'gallery'}})

4.3 UI 中的相册入口

在拍照页面底部,用户也可以点击相册按钮随时切换到相册选择:

// 拍照模式底栏中的相册按钮Text('🖼').fontSize(22).fontColor('rgba(255,255,255,0.8)').onClick(()=>this.pickFromAlbum())

五、MediaLibraryKit 的其他能力

5.1 获取相册列表

asyncfunctiongetAlbums():Promise<photoAccessHelper.Album[]>{consthelper=photoAccessHelper.getPhotoAccessHelper(getContext(this))constalbums=awaithelper.getAlbums(photoAccessHelper.AlbumType.USER,photoAccessHelper.AlbumSubtype.USER_GENERIC)returnalbums}

5.2 保存图片到相册

import{photoAccessHelper}from'@kit.MediaLibraryKit'asyncfunctionsaveToAlbum(uri:string):Promise<void>{consthelper=photoAccessHelper.getPhotoAccessHelper(getContext(this))// 将文件添加到系统相册awaithelper.addAsset(uri)}

六、PhotoViewPicker vs 自定义相册

对比PhotoViewPicker(项目使用)自定义相册
实现复杂度低(3 行代码)高(权限 + 查询 + UI)
UI 一致性系统原生相册自定义 UI
多选支持✅ 内置需要自行实现
权限要求自动处理需要 MANAGE_MEDIA
推荐场景快速集成需定制 UI 的场景

七、路径与 URI 的处理

7.1 路径统一

拍照和相册选择的图片路径格式不同,但后续处理方式一致:

// 拍照:文件系统路径this.previewPath=`/data/storage/el2/base/cache/capture_${Date.now()}.jpg`// 相册:content:// URIthis.previewPath=result.photoUris[0]// content://media/...// 两种路径传入 AnalyzingPage 后的使用constimagePath=AppStorage.get<string>('capture_image_path')??''awaitHandwritingDao.create({image_path:imagePath,// 原样保存路径// ...})

7.2 路径格式统一化

privatenormalizePath(uri:string):string{if(uri.startsWith('content://')){// content:// URI → 复制到应用缓存目录// 这需要读取文件内容并写入临时目录returnuri// 项目当前直接使用 URI}returnuri// 已经是文件路径}

八、权限相关

8.1 相册读取权限

使用 PhotoViewPicker 不需要申请ohos.permission.READ_MEDIA权限——系统选择器自动处理了授权。但如果使用photoAccessHelper的其他 API(如遍历相册),则需要声明权限:

{ "requestPermissions": [ { "name": "ohos.permission.READ_MEDIA", "reason": "$string:media_permission_reason", "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" } } ] }

8.2 权限对比

场景需要权限权限声明
PhotoViewPicker.select()❌ 不需要系统选择器自带授权
photoAccessHelper.getAlbums()✅ 需要ohos.permission.READ_MEDIA
photoAccessHelper.addAsset()✅ 需要ohos.permission.WRITE_MEDIA

总结

本文从 CapturePage 的pickFromAlbum()方法出发,完整解析了鸿蒙 PhotoViewPicker 的使用:

  1. 基本使用new PhotoViewPicker()select(options)result.photoUris
  2. 选项配置maxSelectNumber控制选择数量
  3. 双模式切换:通过source参数在拍照/相册间切换
  4. URI 处理:拍照返回文件路径,相册返回 content:// URI
  5. 权限优势:PhotoViewPicker 不需要额外权限声明

下一篇文章将介绍图片压缩与本地存储——在保存图片时的质量和大小优化。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


参考资源:

  • PhotoViewPicker API
  • [CapturPage 相册选择源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/pages/CapturePage.ets)
  • photoAccessHelper 开发指南
  • @kit.MediaLibraryKit 模块
  • media 权限声明
  • HarmonyOS 开发文档

九、PhotoViewPicker 完整功能扩展

9.1 多选图片场景

asyncfunctionpickMultipleImages():Promise<string[]>{constphotoPicker=newphotoAccessHelper.PhotoViewPicker()constoptions:photoAccessHelper.PhotoSelectOptions={maxSelectNumber:9,// 最多选 9 张MIME:['image/jpeg','image/png','image/heif']// 仅图片格式}constresult=awaitphotoPicker.select(options)returnresult.photoUris??[]}

9.2 DocumentViewPicker:选择文档

除了图片,鸿蒙还提供了 DocumentViewPicker 用于选择文档文件:

import{picker}from'@kit.CoreFileKit'asyncfunctionpickDocument():Promise<string>{constdocumentPicker=newpicker.DocumentViewPicker()constoptions:picker.DocumentSelectOptions={maxSelectNumber:1,fileSuffixFilters:['.pdf','.txt','.docx']// 只显示指定后缀的文件}constresult=awaitdocumentPicker.select(options)returnresult[0]??''}

9.3 URI 转文件路径

相册 URI(content://)需要通过fs.open获取文件描述符才能读取:

importfsfrom'@ohos.file.fs'asyncfunctionreadImageFromUri(uri:string):Promise<ArrayBuffer>{// 通过 URI 打开文件constfile=fs.openSync(uri,fs.OpenMode.READ_ONLY)// 获取文件大小conststat=fs.statSync(file.fd)// 读取文件内容constbuffer=newArrayBuffer(stat.size)fs.readSync(file.fd,buffer)fs.closeSync(file)returnbuffer}

9.4 相册访问能力对比

功能PhotoViewPickerphotoAccessHelperDocumentViewPicker
选择图片❌(需权限查询)
选择文档
遍历相册✅(需 READ_MEDIA)
保存到相册✅(需 WRITE_MEDIA)
权限要求无需额外权限需要 READ_MEDIA无需额外权限
推荐场景用户主动选择程序化批量访问用户选择文档

9.5 相册操作完整代码模板

// 完整的相册选择 + 图片处理模板@Componentstruct ImagePickerDemo{@StateselectedImageUri:string=''@StateisLoading:boolean=falseprivateasyncpickAndProcess():Promise<void>{this.isLoading=truetry{// ① 选择图片constpicker=newphotoAccessHelper.PhotoViewPicker()constresult=awaitpicker.select({maxSelectNumber:1})if(!result.photoUris||result.photoUris.length===0)returnconsturi=result.photoUris[0]// ② 解码 PixelMapconstsource=image.createImageSource(uri)constpixelMap=awaitsource.createPixelMap({desiredSize:{width:1024,height:1024}})// ③ 压缩保存到缓存constoutputPath=`${getContext(this).cacheDir}/selected_${Date.now()}.jpg`constpacker=image.createImagePacker()constbuffer=awaitpacker.packing(pixelMap,{format:'image/jpeg',quality:85})constfile=fs.openSync(outputPath,fs.OpenMode.CREATE|fs.OpenMode.WRITE_ONLY)fs.writeSync(file.fd,buffer)fs.closeSync(file)// ④ 释放内存pixelMap.release()this.selectedImageUri=outputPath}catch(err){hilog.error(0x0000,'ImagePicker','选图失败: %s',JSON.stringify(err))}finally{this.isLoading=false}}build(){Column(){if(this.selectedImageUri){Image(this.selectedImageUri).width(200).height(200).objectFit(ImageFit.Cover)}Button('从相册选择').onClick(()=>this.pickAndProcess())}}}

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!

http://www.jsqmd.com/news/1165909/

相关文章:

  • 2026 智能体全景盘点:六大核心维度下的主流阵营横向对比
  • 上电瞬间的过冲电压烧毁芯片-----原因在这里
  • Chrome-Charset终极指南:3分钟彻底解决网页乱码的完整方案
  • 【GitHub】Meetily:隐私优先的本地 AI 会议助手深度解析
  • Unity移动开发三大核心路径解析:热更新与本地存档的架构基石
  • 美团LongCat-2.0:1M上下文MoE大模型在代码生成中的实践指南
  • 智能体循环工程:四种自动化模式提升AI编程效率
  • 仓库的四季:春进、夏忙、秋囤、冬守
  • 2026年7月最新福州积家官方售后热线及客户服务网点地址 - 积家官方售后服务中心
  • Kimi-k2.5如何实现高精度跨文件代码理解与副作用感知
  • 企业禁用root token却仍遭横向提权?Claude Code权限上下文隔离机制失效的3个隐蔽触发条件(CVE-2024-CODE-012已静默修复)
  • 【计算机大数据毕业设计案例】基于 Python 的黑龙江景区季节热度监测系统的设计与实现 基于 Python 的黑龙江文旅评论文本挖掘系统(程序+文档+讲解+定制)
  • 豆包代码生成能力实测:从语法正确到生产可用的工程化验证体系
  • CycloneDX:全栈软件供应链安全标准解读及优势分析
  • 【数字员工ARM】从“用工具”到“管员工”:AI 落地思维的代际跃迁
  • 靠谱的焦虑失眠调理公司
  • Coze源码分析-资源库-编辑工作流-后端源码-数据存储/安全/错误
  • 记录 NTC 电池 高低温提示
  • AI游戏工作室:48个智能体协同开发Godot游戏的工程实践
  • 豆包社交第一步,为何先找飞书?拆解AI时代最贵的一张聊天网
  • 基于Bluetooth 5.4与PIC32的高保真无线音频系统设计
  • AI 写小说被平台判定低质?番茄 AI 检测原理与去 AI 味实战
  • 2026年7月最新合肥宇舶官方售后客户服务电话及线下网点地址 - 亨得利官方服务中心
  • 腾讯Hy3稀疏MoE模型解析:295B参数架构与256K上下文实战指南
  • StarRocks如何查看一张表是否是单副本?
  • Codex CLI 深度配置与安全沙箱实战指南
  • 2026年7月最新绍兴宇舶官方售后客服服务电话及地址网点大全 - 亨得利官方服务中心
  • n8n定时采集公众号RSS:零代码自动化信息归档方案
  • C++类与对象(上)
  • AI员工从0到1落地指南:职责定义、低代码开发与持续迭代