【鸿蒙心迹】可可图片编辑 HarmonyOS(3)分享图片

【鸿蒙心迹】可可图片编辑 HarmonyOS(3)分享图片

可可图片编辑 HarmonyOS(3)应用间分享图片

前言

可可图标编辑也实现了图片的分享功能,演示效果如下。

Share Kit(分享服务) 介绍

hare Kit(分享服务)为应用提供文本、图片、视频等内容跨应用、跨端分享能力。

应用把需要分享的内容和预览样式配置给Share Kit,Share Kit将根据不同的场景进行使用:

  • 针对应用间分享的场景,根据分享的数据类型、数量等信息构建分享面板,为用户提供内容预览、推荐分享联系人、关联应用及操作界面,便于用户快速选择分享应用或操作,将内容分发到目标应用。
  • 针对跨端分享的场景,根据分享的数据类型、数量等信息构建预览界面,用于跨端分享。
应用类型相关逻辑
宿主应用宿主应用需要对可分享的内容提供分享入口,在用户点击分享时,配置分享内容到分享,拉起系统分享面板。通过分享面板发起分享碰一碰分享
目标应用需要在应用中构建具有数据处理能力组件,包括以下两种分享方式。应用内处理分享内容分享详情页处理分享内容(可选)社交类应用可遵照意图框架接入规范把最近分享行为联系人相关信息捐献到意图框架,Share Kit可从意图框架获取推荐信息,当用户选择推荐的联系人时,会把联系人信息随分享数据一起给到目标应用,目标应用可以根据联系人信息直接一步发送内容给指定用户。

分类

Share Kit 可以分为 systemShare 和 HarmonyShare ,两者区别主要是:

  • systemShare 用于应用之间的分享
  • HarmonyShare 用于设备之间的分享

在实际开发中,两者可能配合使用:

跨端分享:通过 HarmonyShare 发起碰一碰传输,接收端通过 HarmonyShare 的沙箱监听处理数据。

应用内分享:通过 systemShare 构造分享内容,用户选择目标应用后跳转到对应页面。

systemShare 使用步骤

1. 选择图片

  1. 配置图片选择参数
    • 创建 PhotoSelectOptions 对象,设置 MIMETypeIMAGE_TYPE 限制只能选择图片类型文件
    • 通过 maxSelectNumber = 5 限制最大可选择数量,符合鸿蒙系统图片选择器的多选能力
  2. 创建图片选择器实例
    • 使用 PhotoViewPicker 类(鸿蒙媒体访问框架)创建选择器实例,体现鸿蒙API的分层设计
  3. 调用异步选择方法
    • 通过 select()方法触发系统图片选择界面,采用Promise异步处理机制:
      • 成功回调:提取返回结果中的 photoUris 数组,取首元素赋值给组件状态变量 fileUri,并打印完整URI列表
      • 异常处理:捕获 BusinessError 异常,输出包含错误码和消息的日志,符合ArkTS错误处理规范
Button("选择图片").onClick(()=>{// 1 设置选择图片的参数const photoSelectOptions =newphotoAccessHelper.PhotoSelectOptions(); photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;// 过滤选择媒体文件类型为IMAGE。 photoSelectOptions.maxSelectNumber =5;// 选择媒体文件的最大数目。let uris:Array<string>=[];// 2 创建图片选择器const photoViewPicker =newphotoAccessHelper.PhotoViewPicker();// 3 开始选择图片 photoViewPicker.select(photoSelectOptions).then((photoSelectResult: photoAccessHelper.PhotoSelectResult)=>{ uris = photoSelectResult.photoUris;this.fileUri = uris[0]console.info('photoViewPicker.select to file succeed and uris are:'+ uris);}).catch((err: BusinessError)=>{console.error(`Invoke photoViewPicker.select failed, code is ${err.code}, message is ${err.message}`);})})

2. 拷贝到沙箱

  1. 获取沙箱路径
    • 通过 getUIContext().getHostContext() 获取 UIAbility 上下文
    • 使用 cacheDir 属性获取应用专属缓存目录(框架提供的安全存储路径)
  2. 生成唯一文件名
    • 通过 split('.').pop() 解析原始文件的扩展名(ArkTS 的字符串操作特性)
    • 使用 Date.now() 生成时间戳作为文件名前缀,确保文件名唯一性
  3. 执行文件拷贝
    • 使用 fs.openSync 以只读模式打开源文件(ArkTS 文件系统 API)
    • 调用 fs.copyFileSync 执行文件描述符到目标路径的同步拷贝操作
  4. 状态反馈
    • 成功时通过 AlertDialog 显示操作结果(ArkUI 的声明式 UI 组件)
    • 失败时捕获异常并显示错误提示(try-catch 异常处理机制)
  5. 数据绑定更新
    • 将生成的缓存路径 copyFilePath 赋值给 this.cacheUri,用于后续数据绑定或状态管理(响应式编程特性)
Button("复制到沙箱").onClick(async()=>{try{const context =this.getUIContext().getHostContext()as common.UIAbilityContext;const cacheDir = context.cacheDir;const fileType =this.fileUri.split('.').pop()// 后缀名// 生成一个新的文件名const fileName = Date.now()+'.'+ fileType // 通过缓存路径+文件名 拼接出完整的路径const copyFilePath = cacheDir +'/'+ fileName // 将文件 拷贝到 临时目录const file = fs.openSync(this.fileUri, fs.OpenMode.READ_ONLY) fs.copyFileSync(file.fd, copyFilePath) AlertDialog.show({ message:JSON.stringify('写入到沙箱',null,2)})this.cacheUri = copyFilePath }catch(e){ AlertDialog.show({ message:JSON.stringify('写入失败',null,2)})}})

3. 分享

  1. 获取UI上下文
    • 通过this.getUIContext()获取当前UI组件的上下文对象
    • 使用getHostContext()获取UIAbility上下文,用于后续调用系统分享能力
  2. 确定统一数据类型(UTD)
    • 通过文件后缀名(jpg/png/webp)匹配对应的UTD类型ID
    • 使用utd.getUniformDataTypeByFilenameExtensionAPI获取标准数据类型标识
  3. 构建分享数据对象创建 systemShare.SharedData实例,包含:
    • utd:统一数据类型标识
    • title/description:分享时显示的元数据
    • uri:通过fileUri.getUriFromPath转换沙箱路径为可访问URI
  4. 初始化分享控制器创建systemShare.ShareController实例,绑定分享数据
  5. 显示系统分享界面调用 controller.show()步方法,传入:
    • SelectionMode.SINGLE:单选模式
    • SharePreviewMode.DETAIL:详细预览模式
Button("分享").onClick(async()=>{try{// 获取UI上下文const uiContext: UIContext =this.getUIContext();const context: common.UIAbilityContext = uiContext.getHostContext()as common.UIAbilityContext;// 根据图片格式确定UTD类型let utdTypeId:string;switch(this.fileUri.split('.').pop()){case'jpeg':case'jpg': utdTypeId = utd.getUniformDataTypeByFilenameExtension('.jpg', utd.UniformDataType.IMAGE);break;case'png': utdTypeId = utd.getUniformDataTypeByFilenameExtension('.png', utd.UniformDataType.IMAGE);break;case'webp': utdTypeId = utd.getUniformDataTypeByFilenameExtension('.webp', utd.UniformDataType.IMAGE);break;default: utdTypeId = utd.getUniformDataTypeByFilenameExtension('.jpg', utd.UniformDataType.IMAGE);}// 创建分享数据,使用沙箱中的图片URIconst shareData: systemShare.SharedData =newsystemShare.SharedData({ utd: utdTypeId, title:'Picture Title', description:'Picture Description', uri: fileUri.getUriFromPath(this.cacheUri),});// 创建分享控制器const controller: systemShare.ShareController =newsystemShare.ShareController(shareData);// 显示分享界面await controller.show(context,{ selectionMode: systemShare.SelectionMode.SINGLE, previewMode: systemShare.SharePreviewMode.DETAIL});console.log('图片分享成功');}catch(e){console.log('图片分享失败');}})

总结

本文介绍了在鸿蒙应用中实现图片分享功能的完整流程。通过 Share Kit 的 systemShare 服务,开发者可以轻松实现应用间的图片分享。核心实现包括三个步骤:

  1. 图片选择:使用 PhotoViewPicker 调用系统图片选择器,支持多选并限制文件类型
  2. 沙箱拷贝:将选中图片拷贝到应用缓存目录,确保分享时的文件访问权限
  3. 分享操作:通过 ShareController 构建分享数据,调用系统分享面板完成跨应用分享

整个流程充分利用了鸿蒙系统的媒体访问框架、文件系统 API 和分享服务,为用户提供了原生、流畅的图片分享体验。

案例完整代码

import{ photoAccessHelper }from'@kit.MediaLibraryKit';import{ BusinessError }from'@kit.BasicServicesKit';import{ fileUri, fileIo as fs }from'@kit.CoreFileKit';import{ common }from'@kit.AbilityKit';import{ uniformTypeDescriptor as utd }from'@kit.ArkData';import{ systemShare }from'@kit.ShareKit';@ComponentV2@Entry struct Index { fileUri:string="" cacheUri:string=""build(){Column({ space:10}){Button("选择图片").onClick(()=>{// 1 设置选择图片的参数const photoSelectOptions =newphotoAccessHelper.PhotoSelectOptions(); photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;// 过滤选择媒体文件类型为IMAGE。 photoSelectOptions.maxSelectNumber =5;// 选择媒体文件的最大数目。let uris:Array<string>=[];// 2 创建图片选择器const photoViewPicker =newphotoAccessHelper.PhotoViewPicker();// 3 开始选择图片 photoViewPicker.select(photoSelectOptions).then((photoSelectResult: photoAccessHelper.PhotoSelectResult)=>{ uris = photoSelectResult.photoUris;this.fileUri = uris[0]console.info('photoViewPicker.select to file succeed and uris are:'+ uris);}).catch((err: BusinessError)=>{console.error(`Invoke photoViewPicker.select failed, code is ${err.code}, message is ${err.message}`);})})Button("复制到沙箱").onClick(async()=>{try{const context =this.getUIContext().getHostContext()as common.UIAbilityContext;const cacheDir = context.cacheDir;const fileType =this.fileUri.split('.').pop()// 后缀名// 生成一个新的文件名const fileName = Date.now()+'.'+ fileType // 通过缓存路径+文件名 拼接出完整的路径const copyFilePath = cacheDir +'/'+ fileName // 将文件 拷贝到 临时目录const file = fs.openSync(this.fileUri, fs.OpenMode.READ_ONLY) fs.copyFileSync(file.fd, copyFilePath) AlertDialog.show({ message:JSON.stringify('写入到沙箱',null,2)})this.cacheUri = copyFilePath }catch(e){ AlertDialog.show({ message:JSON.stringify('写入失败',null,2)})}})Button("分享").onClick(async()=>{try{// 获取UI上下文const uiContext: UIContext =this.getUIContext();const context: common.UIAbilityContext = uiContext.getHostContext()as common.UIAbilityContext;// 根据图片格式确定UTD类型let utdTypeId:string;switch(this.fileUri.split('.').pop()){case'jpeg':case'jpg': utdTypeId = utd.getUniformDataTypeByFilenameExtension('.jpg', utd.UniformDataType.IMAGE);break;case'png': utdTypeId = utd.getUniformDataTypeByFilenameExtension('.png', utd.UniformDataType.IMAGE);break;case'webp': utdTypeId = utd.getUniformDataTypeByFilenameExtension('.webp', utd.UniformDataType.IMAGE);break;default: utdTypeId = utd.getUniformDataTypeByFilenameExtension('.jpg', utd.UniformDataType.IMAGE);}// 创建分享数据,使用沙箱中的图片URIconst shareData: systemShare.SharedData =newsystemShare.SharedData({ utd: utdTypeId, title:'Picture Title', description:'Picture Description', uri: fileUri.getUriFromPath(this.cacheUri),});// 创建分享控制器const controller: systemShare.ShareController =newsystemShare.ShareController(shareData);// 显示分享界面await controller.show(context,{ selectionMode: systemShare.SelectionMode.SINGLE, previewMode: systemShare.SharePreviewMode.DETAIL});console.log('图片分享成功');}catch(e){console.log('图片分享失败');}})}.width("100%").height("100%").justifyContent(FlexAlign.Center)}}

以往文章

近期活动

最近想要想要考取 HarmonyOS 基础或者高级证书,或者快要获取的同学都可以点击这个链接,加入我的班级,考取成功有机会获得鸿蒙礼盒一份。

Read more

《算法题讲解指南:优选算法-分治-归并》--47.归并排序,48.数组中的逆序对

《算法题讲解指南:优选算法-分治-归并》--47.归并排序,48.数组中的逆序对

🔥小叶-duck:个人主页 ❄️个人专栏:《Data-Structure-Learning》 《C++入门到进阶&自我学习过程记录》《算法题讲解指南》--优选算法 ✨未择之路,不须回头 已择之路,纵是荆棘遍野,亦作花海遨游 目录 47.归并排序 题目链接: 题目描述: 题目示例: 解法(归并排序): 算法思路: C++算法代码: 算法总结及流程解析: 48.数组中的逆序对 题目链接: 题目描述: 题目示例: 解法(利用归并排序的过程——分治): 算法思路: C++算法代码: 算法总结及流程解析: 结束语 47.归并排序 题目链接: 215. 数组912. 排序数组 - 力扣(LeetCode)215.

By Ne0inhk
华为OD机试真题2025双机位C卷 Python&JS 实现【自动泊车】

华为OD机试真题2025双机位C卷 Python&JS 实现【自动泊车】

目录 题目 思路 Code 题目 题目描述 在某商场的地下停车场,部署了一套智能导航系统。停车场可以看作是一个 r*c 的网格矩阵,其中:0 表示该位置是空的行车道,车辆可以通行。1 表示该位置存有障碍物、立柱或其他已停放的车辆,车辆无法通行。 停车场的入口统一设在坐标 [0, 0] 处。现在有一辆车进入停车场,需要前往指定的目标车位 [m, n]。 车辆在停车场内只能沿着上、下、左、右四个方向移动,每移动一个格子计为步数 1。请你帮车主规划一条从入口到目标车位的最短路径。输入描述第一行输入两个整数 m 和 n,表示目标车位的行下标和列下标。第二行输入两个整数 row 和 col,表示停车场的总行数和总列数。接下来的 row 行,每行包含 col

By Ne0inhk
Python 3.12 logging - 07 - LogRecord

Python 3.12 logging - 07 - LogRecord

LogRecord的基本概念 LogRecord 是 Python logging 模块中代表一条日志事件的数据容器。简单来说,它就像一张“记录单”,每当程序调用日志方法(如 logger.info())时,logging 会自动创建一个 LogRecord 对象,并把所有相关信息都装进去,包括:日志消息内容、日志级别(DEBUG/INFO 等)、产生日志的时间戳、源代码位置(文件名、行号、函数名)、当前线程/进程 ID、异常信息(如果有)、其他自定义属性(通过 extra 添加)。 这个“记录单”随后会被传递给日志处理器(Handler)和格式化器(Formatter),最终被转换成我们看到的输出文本。 一、LogRecord类机制解析 LogRecord是logging模块的核心类,用于封装日志事件的所有信息。

By Ne0inhk

涛哥聊Python | 程序员必看:Codex 和 Claude Code 实战对比,差别比你想的更大!

本文来源公众号“涛哥聊Python”,仅用于学术分享,侵权删,干货满满。 原文链接:https://mp.weixin.qq.com/s/NPzwT-5_qt9ncWxYaaQpYg 程序开发,往往不只是思考逻辑,更多时间消耗在那些重复又琐碎的环节,接口需要写一堆模板代码,参数的小改动要牵连多个文件,修个 bug 还得来回补测试,这些工作不难,但却很耗时。 正因为如此,AI 编程助手逐渐进入开发者的日常,它们虽然不能完全替代人类思考,却能帮我们把重复的部分自动化。 在众多工具中,Codex 和 Claude Code 是讨论度最高的两个,一个专注于把自然语言快速翻译成代码,另一个则成为项目里的智能合作者,这两个工具的功能定位不相同,开发者可以根据自己的需求来选择最合适的助手。 Codex:从“人话”到“代码”的翻译官 Codex 的设计思路很直接:把自然语言转化为代码,只要用一句需求,它就能生成相应的实现,

By Ne0inhk