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

【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%')
  }

  @Builder
  PictureBuilder() {
    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: 图片选择博客代码

相关文章:

  • 精选Python小项目代码
  • 【前端框架】深入探讨 Vue 3 组件生命周期的变化和最佳实践
  • 卓越设计彰显品质:福特中国“烈马宇宙”项目展示高质量标准
  • linux--关于GCC、动态库静态库
  • kubectl exec 实现的原理
  • 【SQL技术】不同数据库引擎 SQL 优化方案剖析
  • 30天自制操作系统第一天(1)
  • 微信小程序性能优化
  • 寒假第三周周报
  • 基于JAVA的幼儿园管理系统的设计与实现源码(springboot+vue+mysql)
  • [创业之路-307]:如何解读公司的业绩?它与股价变化的关系?
  • c++中std::thread构造函数的注意事项
  • 【Python】Python入门基础——环境搭建
  • 数据库系统原理——第十章数据恢复技术复习题
  • 学习总结三十四
  • Ubuntu20.04部署stable-diffusion-webui环境小记
  • 题海拾贝:英语作文(map)
  • Selenium定位元素的方法及其语法
  • ubuntu20.04连接airpods pro2
  • LeetCode热题100- 缺失的第一个正数【JavaScript讲解】
  • 上海黄浦江挡潮闸工程建设指挥部成立,组成人员名单公布
  • 中哥两国元首共同见证签署《中华人民共和国政府与哥伦比亚共和国政府关于共同推进丝绸之路经济带和21世纪海上丝绸之路建设的合作规划》
  • 这个“超强致癌细菌”,宝宝感染率高达40%,预防却很简单
  • 香港根据《维护国家安全条例》订立附属法例
  • 第四届长三角国际应急博览会开幕,超3000件前沿装备技术亮相
  • AI观察|从万元到百万元,DeepSeek一体机江湖混战