【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…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...