Harmony OS5——如何生成二维码并执行下载分享

189 阅读2分钟

功能说明:

-   点击"生成分享二维码"按钮显示弹框
-   弹框中显示生成的二维码
-   点击"保存到相册"将二维码保存到设备相册
-   点击"取消"关闭弹框

一. 生成二维码分享弹框

1. 主界面二维码按钮

具体代码如下:

 Column() {
      // 主界面按钮
      Button('生成分享二维码')
        .width(200)
        .height(50)
        .fontSize(16)
        .onClick(() => this.showDialog())
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    

2. 显示弹框

 // 显示弹框
  private async showDialog() {
    await this.generateQRCode();
    if (this.qrCodeImage) {
      this.isDialogShow = true;
    }
  }

二. 弹框中显示生成的二维码

具体代码如下:


  // 二维码弹框
    if (this.isDialogShow) {
      Stack() {
        // 半透明背景
        Column()
          .width('100%')
          .height('100%')
          .backgroundColor('#000000')
          .opacity(0.5)
          .onClick(() => this.closeDialog())
        
        // 弹框内容
        Column() {
          // 标题
          Text('分享二维码')
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
            .margin({ top: 20, bottom: 10 })
          
          // 加载状态
          if (this.isLoading) {
            LoadingProgress()
              .width(60)
              .height(60)
              .margin(20)
          } else if (this.qrCodeImage) {
            // 二维码图片
            Image(this.qrCodeImage)
              .width(280)
              .height(280)
              .margin(10)
              .borderRadius(8)
              .border({ width: 1, color: '#EEEEEE' })
          }
          
        }
        .width('80%')
        .backgroundColor(Color.White)
        .borderRadius(16)
        .alignItems(HorizontalAlign.Center)
        .shadow({ radius: 24, color: '#000000', offsetX: 0, offsetY: 4 })
      }
      .width('100%')
      .height('100%')
      .position({ x: 0, y: 0 })
      .zIndex(1)
    }
  }

三. 将二维码保存到相册

具体代码如下:


  // 保存二维码到相册
  private async saveToAlbum() {
    if (!this.qrCodeImage) return;
    
    const hasPermission = await this.checkPermissions();
    if (!hasPermission) {
      promptAction.showToast({ message: '需要相册权限才能保存', duration: 2000 });
      return;
    }

    this.saveButtonText = '保存中...';
    
    try {
      const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(this.context);
      const fileName = `QRCode_${new Date().getTime()}.jpg`;
      
      // 创建图片文件
      const photoAsset = await phAccessHelper.createAsset(
        photoAccessHelper.PhotoType.IMAGE, 
        fileName,
        { title: fileName }
      );
      
      // 写入文件
      const fd = await photoAsset.open('w');
      const imagePacker = image.createImagePacker();
      const packOpts = { format: 'image/jpeg', quality: 100 };
      const arrayBuffer = await imagePacker.packing(this.qrCodeImage, packOpts);
      await fs.write(fd, arrayBuffer);
      await fs.close(fd);
      
      promptAction.showToast({ message: '二维码已保存到相册', duration: 2000 });
      this.closeDialog();
    } catch (error) {
      console.error('保存失败:', error);
      promptAction.showToast({ message: '保存失败,请重试', duration: 2000 });
    } finally {
      this.saveButtonText = '保存到相册';
    }
  }

四. 关闭弹框

具体代码如下:

   // 操作按钮
          Row() {
            Button('取消')
              .width(120)
              .height(40)
              .fontColor('#333333')
              .backgroundColor('#EEEEEE')
              .onClick(() => this.closeDialog())
            
            Button(this.saveButtonText)
              .width(120)
              .height(40)
              .margin({ left: 20 })
              .backgroundColor('#0A59F7')
              .fontColor(Color.White)
              .enabled(!this.isLoading && this.qrCodeImage !== null)
              .onClick(() => this.saveToAlbum())
          }
          .margin({ top: 20, bottom: 20 })

六. 使用说明

1. 1. 此代码需要以下权限,请确保在module.json5中声明:

具体代码如下:

{
  "requestPermissions": [
    {
      "name": "ohos.permission.READ_IMAGEVIDEO",
      "reason": "需要读取相册"
    },
    {
      "name": "ohos.permission.WRITE_IMAGEVIDEO",
      "reason": "需要保存图片到相册"
    }
  ]
}

2. 需要安装@ohos/qrcode模块

可以通过以下命令安装:

ohpm install @ohos/qrcode

3. 如果要分享其他数据,只需修改qrCodeData的值即可。

七. 优化

权限管理优化

·添加了动态权限检查

·在保存前确认权限状态

·友好的权限提示

具体代码如下:


 // 权限检查
  private async checkPermissions(): Promise<boolean> {
    try {
      const atManager = abilityAccessCtrl.createAtManager();
      const permissions: Array<string> = [
        'ohos.permission.READ_IMAGEVIDEO',
        'ohos.permission.WRITE_IMAGEVIDEO'
      ];
      
      const result = await atManager.requestPermissionsFromUser(this.context, permissions);
      return result.authResults.every(granted => granted);
    } catch (error) {
      console.error('权限检查失败:', error);
      return false;
    }
  }

完整代码如下:

import { QRCode } from '@ohos/qrcode';
import image from '@ohos.multimedia.image';
import photoAccessHelper from '@ohos.file.photoAccessHelper';
import common from '@ohos.app.ability.common';
import promptAction from '@ohos.promptAction';
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';

@Entry
@Component
struct QRShareDialog {
  @State qrCodeData: string = 'https://example.com'; // 默认二维码数据
  @State isDialogShow: boolean = false;
  @State qrCodeImage: image.PixelMap | null = null;
  @State isLoading: boolean = false;
  @State saveButtonText: string = '保存到相册';
  private context = getContext(this) as common.UIAbilityContext;

  // 权限检查
  private async checkPermissions(): Promise<boolean> {
    try {
      const atManager = abilityAccessCtrl.createAtManager();
      const permissions: Array<string> = [
        'ohos.permission.READ_IMAGEVIDEO',
        'ohos.permission.WRITE_IMAGEVIDEO'
      ];
      
      const result = await atManager.requestPermissionsFromUser(this.context, permissions);
      return result.authResults.every(granted => granted);
    } catch (error) {
      console.error('权限检查失败:', error);
      return false;
    }
  }

  // 优化后的二维码生成
  private async generateQRCode() {
    this.isLoading = true;
    try {
      const qrCode = new QRCode();
      const options = {
        width: 300,
        height: 300,
        color: '#000000',
        background: '#FFFFFF',
        margin: 10,
        errorCorrectionLevel: 'H'
      };
      this.qrCodeImage = await qrCode.generate(this.qrCodeData, options);
    } catch (error) {
      console.error('生成二维码失败:', error);
      promptAction.showToast({ message: '生成二维码失败,请重试', duration: 2000 });
    } finally {
      this.isLoading = false;
    }
  }

  // 显示弹框
  private async showDialog() {
    await this.generateQRCode();
    if (this.qrCodeImage) {
      this.isDialogShow = true;
    }
  }

  // 保存二维码到相册
  private async saveToAlbum() {
    if (!this.qrCodeImage) return;
    
    const hasPermission = await this.checkPermissions();
    if (!hasPermission) {
      promptAction.showToast({ message: '需要相册权限才能保存', duration: 2000 });
      return;
    }

    this.saveButtonText = '保存中...';
    
    try {
      const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(this.context);
      const fileName = `QRCode_${new Date().getTime()}.jpg`;
      
      // 创建图片文件
      const photoAsset = await phAccessHelper.createAsset(
        photoAccessHelper.PhotoType.IMAGE, 
        fileName,
        { title: fileName }
      );
      
      // 写入文件
      const fd = await photoAsset.open('w');
      const imagePacker = image.createImagePacker();
      const packOpts = { format: 'image/jpeg', quality: 100 };
      const arrayBuffer = await imagePacker.packing(this.qrCodeImage, packOpts);
      await fs.write(fd, arrayBuffer);
      await fs.close(fd);
      
      promptAction.showToast({ message: '二维码已保存到相册', duration: 2000 });
      this.closeDialog();
    } catch (error) {
      console.error('保存失败:', error);
      promptAction.showToast({ message: '保存失败,请重试', duration: 2000 });
    } finally {
      this.saveButtonText = '保存到相册';
    }
  }

  build() {
    Column() {
      // 主界面按钮
      Button('生成分享二维码')
        .width(200)
        .height(50)
        .fontSize(16)
        .onClick(() => this.showDialog())
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    
    // 二维码弹框
    if (this.isDialogShow) {
      Stack() {
        // 半透明背景
        Column()
          .width('100%')
          .height('100%')
          .backgroundColor('#000000')
          .opacity(0.5)
          .onClick(() => this.closeDialog())
        
        // 弹框内容
        Column() {
          // 标题
          Text('分享二维码')
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
            .margin({ top: 20, bottom: 10 })
          
          // 加载状态
          if (this.isLoading) {
            LoadingProgress()
              .width(60)
              .height(60)
              .margin(20)
          } else if (this.qrCodeImage) {
            // 二维码图片
            Image(this.qrCodeImage)
              .width(280)
              .height(280)
              .margin(10)
              .borderRadius(8)
              .border({ width: 1, color: '#EEEEEE' })
          }
          
          // 操作按钮
          Row() {
            Button('取消')
              .width(120)
              .height(40)
              .fontColor('#333333')
              .backgroundColor('#EEEEEE')
              .onClick(() => this.closeDialog())
            
            Button(this.saveButtonText)
              .width(120)
              .height(40)
              .margin({ left: 20 })
              .backgroundColor('#0A59F7')
              .fontColor(Color.White)
              .enabled(!this.isLoading && this.qrCodeImage !== null)
              .onClick(() => this.saveToAlbum())
          }
          .margin({ top: 20, bottom: 20 })
        }
        .width('80%')
        .backgroundColor(Color.White)
        .borderRadius(16)
        .alignItems(HorizontalAlign.Center)
        .shadow({ radius: 24, color: '#000000', offsetX: 0, offsetY: 4 })
      }
      .width('100%')
      .height('100%')
      .position({ x: 0, y: 0 })
      .zIndex(1)
    }
  }
}

八. 使用建议

  1. 可以根据实际需求调整二维码的大小和边距
  2. 可以添加输入框让用户自定义二维码内容
  3. 可以扩展分享功能,除了保存到相册外,还可以添加直接分享到社交平台
  4. 对于企业应用,可以在二维码中心添加logo