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

【HarmonyOS 5】鸿蒙用户头像编辑功能实践

【HarmonyOS 5】鸿蒙用户头像编辑功能实践

一、前言

1、应用背景

在鸿蒙化开发过程中,我们发现最基本常见的功能–用户头像的编辑,实现方式和Android与IOS有极大的不同。

在实际开发和调研的过程中,我们发现并总结了鸿蒙隐私处理与业内Android和IOS的差异性。发现隐私保护对标其他两个,上升了一个大台阶。并且针对开发者来说,也更加人性化,便利化。

2、业务需求拆解
用户头像编辑功能流程图如下所示:
在这里插入图片描述

(1) 用户首先触发头像编辑功能(如用户点击 “编辑头像” 按钮)。

(2) 用户打开设备相册,选择目标图片
此时会获取用户选择的图片,可能没有选择,用户取消,或者用户没有给权限。

(3) 手势裁剪图片:
进入裁剪界面,支持手势缩放、拖动、旋转图片,划定裁剪区域。

(4) 上传图片至服务器:
裁剪完成后,将图片压缩并上传至服务器,等待返回成功响应。

(5) 更新头像显示

3、技术调研目标
经过完整的需求拆解,实际需要调研的功能点只有三个:
(1)鸿蒙中如何获取用户的图片
(2)鸿蒙中如何实现图片的裁剪
(3)鸿蒙中如何实现图片的手势操控

二、用户相册图片获取的三种方式

1、用户相册图片获取功能的行业技术路线方案对比:
在鸿蒙调研过程中,我们发现,相当于Android和IOS的获取用户相册图片的方式,鸿蒙大有不同。

目前Android获取用户相册图片的技术路线有:
(1)调用系统原生相册,选取图片后传递给三方应用进行图片处理。隐式 Intent 调用系统相册或者SAF(Storage Access Framework )。
(2)申请用户相册权限,获取用户相册内所有的图片,在三方应用自定义的相册界面进行展示和图片选择逻辑。MediaStore 直接查询系统相册。

目前IOS获取用户相册图片的技术路线有:
(1)通过系统提供的控制器直接调用相册,UIImagePickerController(快速选择)
(2)申请用户相册权限,获取用户相册内所有的图片,在三方应用自定义的相册界面进行展示和图片选择逻辑。通过 PHPhotoLibrary 框架直接访问相册数据库。

当然Android和IOS集成三方SDK也可实现获取用户相册图片,但是其实最终原理还是以上,所以不单独列出。

目前在鸿蒙中对于用户图片的获取有以下三种方式:
(1)需要三方应用申请受限权限,获取文件读写的权限,(调用需要ohos.permission.READ_IMAGEVIDEO权限),这样就可以读取相册媒体库中的媒体资源。

(2)通过鸿蒙系统提供的安全控件PhotoAccessHelper,用户触发操作后即表示同意授权,不需要三方应用再去授权,可以将图片临时授权给应用处理。

(3)针对高度定制化三方应用的需求,不希望相册界面使用系统组件,保持APP的美观和一致性。鸿蒙提供了AlbumPicker,开发者可以在布局中嵌入AlbumPickerComponent组件,通过此组件,应用无需申请权限,即可访问公共目录中的相册列表。

2、用户相册图片获取功能的技术选项
综上所述,我们可以对比发现。鸿蒙在针对用户隐私保护上,比Android和IOS做的都好。极大的保护了用户的隐私安全。

虽然IOS使用系统控制器的方式也可达到鸿蒙的效果,但是市面上既有的APP几乎都是采用,先进入自己应用的相册,然后调用控制器,逻辑操作繁琐,并且很多APP没有在自己的应用相册界面中添加触发【+】加号入口。目前微信是有做,像饿了么京东都没做。需要去系统设置中自己手动添加可以访问的图片给应用。

在这里插入图片描述

说实话,我在使用IOS手机时,就喜欢权限设置里带的访问选择图片功能。不像安卓一样,获取用户授权后,APP就能访问到用户相册所有的图片。而是用户勾选开发给APP的图片,APP只能访问这些。这是IOS的做法。当然IOS也保留了,和Android类似的所有图片开放权限。

获取相册所有图片,是开发者最常见的操作了,目前华为是不提倡APP访问用户所有相册资源。鸿蒙的隐私保护效果好,但是对于开发者就有点痛苦了,特别是产品的要求要与Android和IOS一致的情况下。

DEMO验证阶段我们发现,鸿蒙方案一,申请读取权限,该权限是管制权限,需要三方应用去通过场景申请,非常严格并且几乎无法申请通过。【申请使用受限权限】 所以PASS。

"requestPermissions": [{"name": "ohos.permission.READ_IMAGEVIDEO","usedScene": {"abilities": ["EntryAbility"],"when": "inuse"},"reason": "$string:CAMERA"}
]
  //  创建申请权限明细async reqPermissionsFromUser(): Promise<number[]> {let context = getContext() as common.UIAbilityContext;let atManager = abilityAccessCtrl.createAtManager();let grantStatus = await atManager.requestPermissionsFromUser(context, ['ohos.permission.READ_IMAGEVIDEO']);return grantStatus.authResults;}// 用户申请权限async requestPermission() {let grantStatus = await this.reqPermissionsFromUser();for (let i = 0; i < grantStatus.length; i++) {if (grantStatus[i] === 0) {// 用户授权,可以继续访问目标操作}}}

鸿蒙方案二在930阶段启用,使用也很方便,虽然损失了APP的美观和一致性。使用系统提供的Picker组件,以弹框的形式显示,让用户选择图片,点击完成后,自动收起。效果如下图所示:
在这里插入图片描述

/*** 相册选择图片*/
private async getPictureFromAlbum() {// 创建一个 PhotoSelectOptions 对象,用于配置相册选择的相关选项let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();// 设置选择的文件 MIME 类型为图片类型,这样在相册选择时只会显示图片文件PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;// 设置最大选择数量为 1,即只能从相册中选择一张图片PhotoSelectOptions.maxSelectNumber = 1;// 设置推荐选项,这里指定推荐类型为二维码或条形码,可能会优先展示符合此类型的图片PhotoSelectOptions.recommendationOptions = {recommendationType: photoAccessHelper.RecommendationType.QR_OR_BAR_CODE}// 创建一个 PhotoViewPicker 对象,用于启动相册选择器let photoPicker = new photoAccessHelper.PhotoViewPicker();// 调用 select 方法,传入配置好的选项,等待用户从相册中选择图片// 返回一个 PhotoSelectResult 对象,包含用户选择的图片的相关信息let photoSelectResult: photoAccessHelper.PhotoSelectResult = await photoPicker.select(PhotoSelectOptions);// 从 PhotoSelectResult 对象中获取用户选择的第一张图片的 URI 路径let albumPath = photoSelectResult.photoUris[0];// 在控制台输出日志,记录获取到的图片路径,方便调试和查看信息console.info(this.TAG, 'getPictureFromAlbum albumPath= ' + albumPath);// 调用 getImageByPath 方法,传入图片路径,用于根据路径获取图片的具体内容await this.getImageByPath(albumPath);
}

方案三是今年系统API升级后公开提供的API,从应用市场下载APP操作对比,使用上看应该是去年给大厂APP,微信微博他们先使用后,才公开的方案。我是比较推荐该方案,搞定定制化,符合APP的整体调性。效果如下图所示:
在这里插入图片描述

// 从 @ohos.file.PhotoPickerComponent 模块导入所需的类和类型
// 这些类和类型用于构建和配置图片选择器组件
import {PhotoPickerComponent, // 图片选择器组件类PickerController, // 图片选择器控制器类,用于控制组件行为PickerOptions, // 图片选择器的配置选项类DataType, // 数据类型枚举BaseItemInfo, // 基础项信息类ItemInfo, // 项信息类,包含更详细的项信息PhotoBrowserInfo, // 图片浏览器信息类ItemType, // 项类型枚举ClickType, // 点击类型枚举MaxCountType, // 最大数量类型枚举PhotoBrowserRange, // 图片浏览器范围枚举ReminderMode, // 提醒模式枚举
} from '@ohos.file.PhotoPickerComponent';
// 导入照片访问辅助工具模块
import photoAccessHelper from '@ohos.file.photoAccessHelper';// 标记为页面入口组件

// 定义一个名为 AlbumTestPage 的组件

struct AlbumTestPage {// 组件初始化时设置参数信息// 创建一个 PickerOptions 实例,用于配置图片选择器的各种选项pickerOptions: PickerOptions = new PickerOptions();// 组件初始化完成后,可控制组件部分行为// 使用 @State 装饰器,使 pickerController 成为响应式状态变量// 创建一个 PickerController 实例,用于控制图片选择器的行为 pickerController: PickerController = new PickerController();// 已选择的图片// 使用 @State 装饰器,使 selectUris 成为响应式状态变量// 用于存储已选择图片的 URI 数组 selectUris: Array<string> = new Array<string>();// 目前选择的图片// 使用 @State 装饰器,使 currentUri 成为响应式状态变量// 用于存储当前选中图片的 URI currentUri: string = '';// 是否显示大图// 使用 @State 装饰器,使 isBrowserShow 成为响应式状态变量// 用于控制是否显示图片浏览器(大图模式) isBrowserShow: boolean = false;// 组件即将显示时调用的生命周期函数aboutToAppear() {// 设置 picker 宫格页数据类型// 将选择器的 MIME 类型设置为图片和视频类型,即图片和视频都会在选择器中显示this.pickerOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE; // 最大选择数量// 设置图片选择的最大数量为 5 张this.pickerOptions.maxSelectNumber = 5;// 超出最大选择数量时// 当选择数量超过最大限制时,以 Toast 形式提醒用户this.pickerOptions.maxSelectedReminderMode = ReminderMode.TOAST;// 是否展示搜索框,默认 false// 开启选择器中的搜索框功能this.pickerOptions.isSearchSupported = true;// 是否支持拍照,默认 false// 开启选择器中的拍照功能this.pickerOptions.isPhotoTakingSupported = true;}// 资源被选中回调,返回资源的信息,以及选中方式// 当图片选择器中的项被点击时触发的回调函数private onItemClicked(itemInfo: ItemInfo, clickType: ClickType): boolean {// 若传入的项信息为空,则直接返回 falseif (!itemInfo) {return false;}// 获取项的类型let type: ItemType | undefined = itemInfo.itemType;// 获取项的 URIlet uri: string | undefined = itemInfo.uri;// 若项类型为相机if (type === ItemType.CAMERA) {// 点击相机 item// 返回 true 则拉起系统相机,若应用需要自行处理则返回 falsereturn true; } else {// 若点击类型为选中if (clickType === ClickType.SELECTED) {// 应用做自己的业务处理if (uri) {// 将选中图片的 URI 添加到已选择数组中this.selectUris.push(uri);// 更新选择器的预选中 URI 数组this.pickerOptions.preselectedUris = [...this.selectUris];}// 返回 true 则勾选,否则则不响应勾选return true; } else {if (uri) {// 若点击类型为取消选中,从已选择数组中过滤掉该 URIthis.selectUris = this.selectUris.filter((item: string) => {return item != uri;});// 更新选择器的预选中 URI 数组this.pickerOptions.preselectedUris = [...this.selectUris];}}return true;}}// 进入大图的回调// 当进入图片浏览器(大图模式)时触发的回调函数private onEnterPhotoBrowser(photoBrowserInfo: PhotoBrowserInfo): boolean {// 设置显示大图标志为 truethis.isBrowserShow = true;return true;}// 退出大图的回调// 当退出图片浏览器(大图模式)时触发的回调函数private onExitPhotoBrowser(photoBrowserInfo: PhotoBrowserInfo): boolean {// 设置显示大图标志为 falsethis.isBrowserShow = false;return true;}// 接收到该回调后,便可通过 pickerController 相关接口向 picker 发送数据,在此之前不生效// 当图片选择器控制器准备好时触发的回调函数private onPickerControllerReady(): void {// 这里可以添加向选择器发送数据的逻辑}// 大图左右滑动的回调// 当在图片浏览器(大图模式)中左右滑动图片时触发的回调函数private onPhotoBrowserChanged(browserItemInfo: BaseItemInfo): boolean {// 更新当前选中图片的 URIthis.currentUri = browserItemInfo.uri ?? '';return true;}// 已勾选图片被删除时的回调// 当已勾选的图片被删除时触发的回调函数private onSelectedItemsDeleted(baseItemInfos: Array<BaseItemInfo>): void 

相关文章:

  • 基于【抖音弹幕抓取数据推送】——制作抖音消息分类查看界面
  • YOLOv8的Python基础--函数篇
  • B站pwn教程笔记-6
  • Linux[Makefile]
  • Vue3路由模式为history,使用nginx部署上线后刷新404的问题
  • Leetcode - 周赛448
  • PostgreSQL数据库的array类型
  • 密码工具类-生成随机密码校验密码强度是否满足要求
  • GPS定位方案
  • 使用阿里AI的API接口实现图片内容提取功能
  • three.js通过GEO数据生成3D地图
  • 2025年5月HCIP题库(带解析)
  • 基于计算机视觉的试卷答题区表格识别与提取技术
  • js var a=如果ForRemove=true,是“normal“,否则为“bold“
  • 网页版部署MySQL + Qwen3-0.5B + Flask + Dify 工作流部署指南
  • 自定义SpringBoot Starter-笔记
  • 当K8S容器没有bash时高阶排查手段
  • Github上如何准确地搜索开源项目
  • (二)毛子整洁架构(CQRS/Dapper/DomianEvent Handler)
  • 8.软考高项(信息系统项目管理师)-沟通管理
  • 习近平向“和平薪火 时代新章——纪念中国人民抗日战争和苏联伟大卫国战争胜利80周年中俄人文交流活动”致贺信
  • 秦洪看盘|受阻回落,蓄积新做多能量
  • 陕南多地供水形势严峻:有的已呼吁启用自备水井
  • 41年轮回,从洛杉矶奔向洛杉矶,李宁故地重游再出发
  • 日本儿童人数已连续44年减少,少子化问题越发严重
  • 演员扎堆音乐节,是丰富了舞台还是流量自嗨?