【HarmonyOS Next】图片选择方案

2025-02-17 09:20:58
183次阅读
0个评论

背景


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

项目架构

2.png

  • 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: 图片选择博客代码

收藏00

登录 后评论。没有帐号? 注册 一个。