【HarmonyOS Next】图片选择方案
背景
封装一个选择图片和调用拍照相机的按钮,展示api13下选择图片和调用相机,可以使用不申请用户权限的方式,进行图片的选择和修改。但是,目前方案并未包含上传图片保存的功能,仅提供图片选择或者拍照后,图片展示的一种方案。

项目架构

- Common :公共操作类存放文件夹
- PromptActionClass:全局弹窗操作类
- components:公共弹窗组件文件夹
- SelectImageDialog:选择图片弹窗组件
- pages->Index:入口界面
重要方法解析
调用相机拍照
- 添加camera, cameraPicker的外部引用
import { camera, cameraPicker } from '@kit.CameraKit';
- 使用cameraPicker的pick方法实现安全调用设备相机,并返回选择结果cameraPicker.PickerResult对象,通过设置cameraPicker.PickerProfile对象属性实现对相机的初始化属性设置。
try {//配置相机设置let pickerProfile: cameraPicker.PickerProfile = {cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK,};let result: cameraPicker.PickerResult =await cameraPicker.pick(getContext(), [cameraPicker.PickerMediaType.PHOTO],pickerProfile);if (result.resultCode == 0) {await this.UpdateShowImage(result.resultUri);}PromptActionClass.CloseDialog();return true;
} catch (e) {console.info(e);return false;
}
访问图库选择图片
- 添加PromptActionClass的外部引用
import { PromptActionClass } from '../Common/PromptActionClass';
- 使用photoAccessHelper.PhotoViewPicker对象的select方法,实现安全调用相册并选择图片。通过photoAccessHelper.PhotoSelectOptions对象,对选择方法进行初始化,可以设置默认选择、选择数量、选择类型等。
try {const photoSelectOpt = new photoAccessHelper.PhotoSelectOptions();//设置选择类型photoSelectOpt.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;//选择图片最大数量photoSelectOpt.maxSelectNumber = 1;//图片选择器const photoPicker = new photoAccessHelper.PhotoViewPicker();const selectResult: photoAccessHelper.PhotoSelectResult = await photoPicker.select(photoSelectOpt)let uri: string = "";if (selectResult.isOriginalPhoto || selectResult.photoUris.length == 0) {return false;}uri = selectResult.photoUris[0];await this.UpdateShowImage(uri);PromptActionClass.CloseDialog();return true;
} catch (e) {console.info(e);return false;
}
整体代码
Index
import { image } from '@kit.ImageKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo } from '@kit.CoreFileKit';
import { PromptActionClass } from '../Common/PromptActionClass';
import { SelectImageDialog } from '../components/SelectImageDialog';
import { camera, cameraPicker } from '@kit.CameraKit';@Entry
@ComponentV2
struct Index {@Local ShowImage: ResourceStr | PixelMap = $r('app.media.AddImageIcon')aboutToAppear(): void {PromptActionClass.SetContext(this.getUIContext());PromptActionClass.SetOptions({builder: () => {this.PictureBuilder()},alignment: DialogAlignment.Bottom,cornerRadius: {topLeft: 20,topRight: 20,bottomLeft: 20,bottomRight: 20},height: 154,width: "90%",})}build() {RelativeContainer() {Button() {Image(this.ShowImage).width("100%").borderRadius(20).padding(10)}.width(120).height(120).type(ButtonType.Normal).backgroundColor(Color.White).borderWidth(3).borderColor('#592708').borderRadius(20).id("AddImageBtn").alignRules({middle: { anchor: "__container__", align: HorizontalAlign.Center }}).margin({ top: 20 }).onClick(() => {PromptActionClass.OpenDialog();})}.height('100%').width('100%')}@BuilderPictureBuilder() {SelectImageDialog({CancelEvent: async () => {try {PromptActionClass.CloseDialog();return true;} catch (e) {console.info(e);return false;}},TakePictureEvent: async () => {try {//配置相机设置let pickerProfile: cameraPicker.PickerProfile = {cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK,};let result: cameraPicker.PickerResult =await cameraPicker.pick(getContext(), [cameraPicker.PickerMediaType.PHOTO],pickerProfile);if (result.resultCode == 0) {await this.UpdateShowImage(result.resultUri);}PromptActionClass.CloseDialog();return true;} catch (e) {console.info(e);return false;}},SelectedPictureEvent: async () => {try {const photoSelectOpt = new photoAccessHelper.PhotoSelectOptions();//设置选择类型photoSelectOpt.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;//选择图片最大数量photoSelectOpt.maxSelectNumber = 1;//图片选择器const photoPicker = new photoAccessHelper.PhotoViewPicker();const selectResult: photoAccessHelper.PhotoSelectResult = await photoPicker.select(photoSelectOpt)let uri: string = "";if (selectResult.isOriginalPhoto || selectResult.photoUris.length == 0) {return false;}uri = selectResult.photoUris[0];await this.UpdateShowImage(uri);PromptActionClass.CloseDialog();return true;} catch (e) {console.info(e);return false;}}})}async UpdateShowImage(uri: string) {let file = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY)const imageSourceApi = image.createImageSource(file.fd);let map: PixelMap = await imageSourceApi.createPixelMap();this.ShowImage = map;}
}
PromptActionClass
import { promptAction } from "@kit.ArkUI";
import { BusinessError } from "@kit.BasicServicesKit";/*** 弹窗操作类*/
export class PromptActionClass {/***展示界面的ID集合*/private static ShowIDArray: number[] = [];static Context: UIContext;/*** 弹窗界面设置*/static Options: promptAction.CustomDialogOptions;static SetContext(context: UIContext) {PromptActionClass.Context = context;}static SetOptions(options: promptAction.CustomDialogOptions) {PromptActionClass.Options = options;}/*** 弹窗*/static OpenDialog() {if (PromptActionClass.Options) {PromptActionClass.Context.getPromptAction().openCustomDialog(PromptActionClass.Options).then((id: number) => {PromptActionClass.ShowIDArray.push(id);console.info('弹窗已打开')}).catch((error: BusinessError) => {let message = (error as BusinessError).message;let code = (error as BusinessError).code;console.error(`弹窗失败,错误代码是:${code}, message 是 ${message}`);})}}/*** 关闭弹窗*/static CloseDialog() {if (PromptActionClass.ShowIDArray.length != 0) {try {PromptActionClass.Context.getPromptAction().closeCustomDialog(PromptActionClass.ShowIDArray[PromptActionClass.ShowIDArray.length-1])console.info('成功关闭弹窗.')} catch {(error: BusinessError) => {let message = (error as BusinessError).message;let code = (error as BusinessError).code;console.error(`弹窗关闭失败,错误代码:${code}, message 是 ${message}`);}}}}
}
SelectImageDialog
@ComponentV2
export struct SelectImageDialog {@Event TakePictureEvent: () => Promise<boolean> = async () => {return false;}@Event SelectedPictureEvent: () => Promise<boolean> = async () => {return false;}@Event CancelEvent: () => Promise<boolean> = async () => {return false;}build() {RelativeContainer() {Button("拍照").type(ButtonType.Normal).width("100%").id("TakePictureBtn").backgroundColor("#ffffff").height(50).fontColor("#343434").alignRules({bottom: { anchor: "SelectedPictureBtn", align: VerticalAlign.Top }}).onClick(async () => {await this.TakePictureEvent();})Button("从相册中选择").type(ButtonType.Normal).width("100%").height(50).id("SelectedPictureBtn").backgroundColor("#ffffff").fontColor("#343434").borderWidth({ bottom: 2, top: 2 }).borderColor("#f6f6f6").alignRules({center: { anchor: "__container__", align: VerticalAlign.Center }}).onClick(async () => {await this.SelectedPictureEvent();})Button("取消").width("100%").type(ButtonType.Normal).height(50).backgroundColor("#ffffff").fontColor("#aeaeae").alignRules({top: { anchor: "SelectedPictureBtn", align: VerticalAlign.Bottom }}).onClick(async () => {await this.CancelEvent();})}.height("100%").width("100%")}
}
图片资源
从绑定资源中下载
代码文件下载
ImageSelectDemo: 图片选择博客代码
相关文章:
【HarmonyOS Next】图片选择方案
背景 封装一个选择图片和调用拍照相机的按钮,展示api13下选择图片和调用相机,可以使用不申请用户权限的方式,进行图片的选择和修改。但是,目前方案并未包含上传图片保存的功能,仅提供图片选择或者拍照后,图…...
【C语言】移除元素
移除元素 给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后…...
Python----Python高级(网络编程:网络高级:多播和广播,C/S架构,TCP,UDP,网络编程)
一、多播和广播 1.1、多播 1.1.1、定义 多播(Multicast)也称为组播,是一种一对多的通信方式,将信息从单个源发送到 多个特定的接收者。这些接收者组成一个特定的多播组,只有加入该组的设备才会接 收和处理多播数据。…...
CES 2025 上的创新方案——无电池智能纸尿裤-AP4470
这款纸尿裤采用了可重复使用的组件,通过检测液体的存在来增强老年人和婴儿的护理,即使电极上滴了几滴液体也是如此。 其原理为尿液中的水分作为电解液,将尿布里安装的两种导电性材料作为正负极,充当电池,从而产生300m…...
Java 运算符
运算符用于对变量和值执行运算。 在下面的示例中,我们使用 运算符将两个值相加: int x 100 50;尽管经常使用运算符将两个值加在一起,例如在上面的示例中,但是它也可以用于将一个变量和一个值或一个变量和另一个变量加在一…...
【一文读懂】什么是MVVM?
MVVM Vue 是一个渐进式的 JavaScript 框架,它采用了 MVVM(Model-View-ViewModel)设计模式,这使得它能够高效地进行数据绑定和用户界面的更新。 概念 1. Model(模型) 含义:Model 代表应用程序…...
GCD of Subset
法1: const int N1e6; signed main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int n,k; cin>>n>>k;vector<int>a(n1),cnt(N10);/*桶cnt不要用map实现!!!速度太慢*/vector<vector<int>>…...
BY组态:工业自动化的未来,触手可及
在工业4.0的浪潮下,智能化、数字化已成为制造业发展的核心驱动力。作为工业自动化领域的重要工具,组态软件在实现设备监控、数据采集、流程控制等方面发挥着不可替代的作用。然而,传统的组态软件往往存在开发周期长、学习成本高、灵活性不足等…...
DeepSeek在linux下的安装部署与应用测试
结合上一篇文章,本篇文章主要讲述在Redhat linux环境下如何部署和使用DeepSeek大模型,主要包括ollama的安装配置、大模型的加载和应用测试。关于Open WebUI在docker的安装部署,Open WebUI官网也提供了完整的docker部署说明,大家可…...
华为昇腾920b服务器部署DeepSeek翻车现场
最近到祸一台HUAWEI Kunpeng 920 5250,先看看配置。之前是部署的讯飞大模型,发现资源利用率太低了。把5台减少到3台,就出了他 硬件配置信息 基本硬件信息 按照惯例先来看看配置。一共3块盘,500G的系统盘, 2块3T固态…...
JavaScript 内置对象-数组对象
在JavaScript中,数组(Array)是一种非常重要的数据结构,它允许我们以列表的形式存储多个值,并提供了丰富的内置方法来操作这些值。无论是处理简单的数值集合还是复杂的对象数组,数组对象都能提供强大的支持。…...
Qt——连接MySQL数据库之ODBC的方法详细总结(各版本大同小异,看这一篇就够了)
【系列专栏】:博主结合工作实践输出的,解决实际问题的专栏,朋友们看过来! 《项目案例分享》 《极客DIY开源分享》 《嵌入式通用开发实战》 《C++语言开发基础总结》 《从0到1学习嵌入式Linux开发》 《QT开发实战》 《Android开发实战》 《实用硬件方案设计》 《结构建模设…...
进程令牌:Windows 安全架构中的关键元素
一、进程令牌概述 进程令牌(Process Token)是 Windows 操作系统中一个重要的安全机制,它包含了与进程安全上下文相关的详细信息。每个进程在执行时都会关联一个进程令牌,令牌用于确定该进程可以访问哪些资源以及能执行哪些操作。…...
基于springboot的超时代停车场管理平台(源码+文档)
大家好我是风歌,曾担任某大厂java架构师,如今专注java毕设领域。今天要和大家聊的是一款基于springboot的超时代停车场管理平台。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 基于Spring Boot的超时代停车场…...
缓存穿透、缓存击穿、缓存雪崩的区别与解决方案
1. 缓存穿透(Cache Penetration) 定义:大量请求查询 数据库中不存在的数据,导致请求绕过缓存直接访问数据库,造成数据库压力过大。 场景: 恶意攻击:例如用不存在的用户ID频繁请求。 业务误操作…...
箭头函数的this指向谁
先看1个重要原则: 由Vue管理的函数,一定不要写箭头函数,箭头函数的this就不再是Vue实例了 箭头函数的 this 指向在定义时确定,继承自外层作用域(即定义时的上下文)的 this,且无法通过 call、app…...
【愚公系列】《Python网络爬虫从入门到精通》012-字符串处理
标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,亚马逊技领云博主,51CTO博客专家等。近期荣誉2022年度…...
【Python】01-基础
目录 1、命令行简介1.1 命令行结构1.2 常用dos指令 2、环境变量2.1 查看2.2 添加2.3 修改2.4 删除 3、path环境变量4、进制5、文本文件和字符集6、Sublime使用 1、命令行简介 命令行就是文本交互页面,通过命令行可以使用指令来操作计算机 1.1 命令行结构 版本及版…...
Java-DFS(深度优先搜索)
原理 深度优先搜索的基本思路是从一个节点开始,依次访问它的每一个邻居节点,直到达到一个没有未被访问的邻居的节点为止。这个过程可以使用递归或者栈来实现。其特点是尽可能深入每一个分支,然后再回溯。 DFS算法常用于解决以下类型的问题&…...
AI大模型编程能力对比:DeepseekClaudeGemini
在当今快速发展的技术领域,人工智能(AI)模型在编程和数据处理方面的应用越来越广泛。不同的AI模型因其独特的设计理念和技术优势,适用于不同的编程任务和场景。 本文将对三种主流的AI模型——DeepSeek v3、Gemini Flash 2.0 和 C…...
【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
【无标题】湖北理元理律师事务所:债务优化中的生活保障与法律平衡之道
文/法律实务观察组 在债务重组领域,专业机构的核心价值不仅在于减轻债务数字,更在于帮助债务人在履行义务的同时维持基本生活尊严。湖北理元理律师事务所的服务实践表明,合法债务优化需同步实现三重平衡: 法律刚性(债…...
