HarmonyOS ArkTS 实战:从零实现二维码生成与扫描工具应用

目录


一、项目背景与效果预览

1.1 痛点场景

日常学习生活中,分享网址、添加好友、连接WiFi、交换名片,二维码无处不在。但手机中缺乏一个轻量、干净、功能全面的二维码工具。本应用集二维码生成、美化、扫描、历史管理、批量生成、模板于一体,支持文本、网址、名片、WiFi等多种类型,并可保存分享,是日常效率利器。

1.2 运行效果(模拟器预览)

  • 主界面(生成):顶部标题,输入框(支持文本/网址),类型选择(文本/网址/名片/WiFi),颜色选择器,Logo开关;中央展示生成的二维码(使用Emoji或Canvas模拟);下方保存、分享、复制按钮。
  • 底部Tabs:“生成”、“扫描”、“历史”、“批量”。
  • 扫描:显示取景框模拟界面,底部有“开始扫描”按钮(模拟识别),同时提供手电筒开关(模拟)。
  • 历史:展示所有生成记录,可滑动删除,点击可重新生成。
  • 批量:输入多个内容(换行分隔),一键生成多个二维码,批量保存。
  • 交互反馈:生成动画、保存Toast、复制剪贴板、清除历史确认。

主题色采用深灰(#1F2937 → #4B5563),体现工具的专业与稳重。


二、技术栈与开发环境

技术项 说明
开发语言 ArkTS
UI 框架 ArkUI 声明式开发
状态管理 @State / @Provide
布局方式 Column + List + Tabs + Stack
数据持久化 @ohos.data.preferences
弹窗/提示 @ohos.prompt / @ohos.dialog
路由管理 @ohos.router
剪贴板 @ohos.pasteboard
相机(模拟) 权限申请 + 预览界面
Canvas绘图 用于二维码模拟绘制
开发工具 DevEco Studio 5.0+
SDK 版本 API 24 及以上

三、需求分析与功能架构

3.1 核心功能清单

  1. 二维码生成:输入文本/网址,选择类型(文本、网址、名片、WiFi),生成二维码(Canvas模拟)。
  2. 美化自定义:可更改二维码颜色,添加Logo(占位),圆角/样式调整。
  3. 保存到相册:将生成的二维码图片保存至系统相册(模拟保存)。
  4. 分享:通过系统分享面板分享二维码图片(模拟)。
  5. 扫描识别:调用相机扫描二维码(提供模拟界面,可扩展真实扫描)。
  6. 历史记录:自动记录所有生成内容,支持查看、删除、重新生成。
  7. 批量生成:一次输入多行内容,生成多个二维码。
  8. 模板快速生成:内置名片模板(姓名、电话、邮箱)和WiFi模板(SSID、密码)。
  9. 剪贴板识别:从剪贴板读取内容快速生成。
  10. 数据持久化:历史记录保存在Preferences,重启不丢失。

3.2 数据流

输入内容 → 生成二维码(Canvas) → 显示预览 → 保存/分享/复制 → 写入历史(持久化)

四、数据结构与服务层设计

4.1 数据模型(完整定义)

// model/QRCodeItem.ets
export interface QRCodeItem {
  id: number;
  content: string;
  type: 'text' | 'url' | 'contact' | 'wifi';
  createTime: number;
  color: string;
  withLogo: boolean;
}

// model/Contact.ets (名片模板)
export interface Contact {
  name: string;
  phone: string;
  email: string;
}

// model/WifiConfig.ets
export interface WifiConfig {
  ssid: string;
  password: string;
  encryption: 'WPA' | 'WEP' | 'nopass';
}

4.2 服务层(Service)

// service/BaseService.ets(同前,略)
// service/QRCodeService.ets
import { BaseService } from './BaseService';
import { QRCodeItem } from '../model/QRCodeItem';

class QRCodeService extends BaseService<QRCodeItem> {
  constructor() { super('QRCodesPrefs', 'history'); }

  async fetch(): Promise<QRCodeItem[]> {
    const data = await this.loadData();
    return data;
  }

  async add(item: QRCodeItem): Promise<QRCodeItem[]> {
    const list = await this.loadData();
    // 去重:如果相同内容相同类型已存在,不重复添加,但可以更新颜色
    const existIdx = list.findIndex(h => h.content === item.content && h.type === item.type);
    if (existIdx !== -1) {
      list[existIdx] = { ...list[existIdx], color: item.color, withLogo: item.withLogo, createTime: Date.now() };
      await this.saveData(list);
      return list;
    }
    list.unshift(item);
    await this.saveData(list);
    return list;
  }

  async delete(id: number): Promise<QRCodeItem[]> {
    const list = await this.loadData();
    const filtered = list.filter(h => h.id !== id);
    await this.saveData(filtered);
    return filtered;
  }

  async clearAll(): Promise<QRCodeItem[]> {
    await this.saveData([]);
    return [];
  }
}
export const qrCodeService = new QRCodeService();

五、核心功能实现(完整代码)

5.1 页面状态与数据加载(主页面 Index.ets)

使用 Tabs 实现生成、扫描、历史、批量四个页面。主页面为生成。

// pages/Index.ets
import { QRCodeItem } from '../model/QRCodeItem';
import { Contact, WifiConfig } from '../model/QRCodeItem';
import { qrCodeService } from '../service/QRCodeService';
import prompt from '@ohos.prompt';
import pasteboard from '@ohos.pasteboard';

@Entry
@Component
struct Index {
  @State history: QRCodeItem[] = [];
  @State currentTab: number = 0;

  // 生成相关
  @State inputText: string = 'https://developer.huawei.com';
  @State qrType: string = 'url';
  @State qrColor: string = '#1F2937';
  @State withLogo: boolean = false;
  @State generatedContent: string = '';

  // 扫描相关(模拟)
  @State isScanning: boolean = false;
  @State scanResult: string = '';
  @State torchOn: boolean = false;

  // 批量生成
  @State batchInput: string = '';
  @State batchResults: string[] = [];

  // 名片模板
  @State contactName: string = '';
  @State contactPhone: string = '';
  @State contactEmail: string = '';

  // WiFi模板
  @State wifiSSID: string = '';
  @State wifiPass: string = '';
  @State wifiEncryption: string = 'WPA';

  @State isLoading: boolean = true;

  aboutToAppear() {
    this.loadData();
  }

  async loadData() {
    this.isLoading = true;
    try {
      this.history = await qrCodeService.fetch();
    } catch (e) {
      prompt.showToast({ message: '加载历史失败' });
    } finally {
      this.isLoading = false;
    }
  }

  // 生成二维码(触发绘制)
  private generateQR() {
    let content = this.inputText.trim();
    if (!content) {
      prompt.showToast({ message: '请输入内容' });
      return;
    }
    // 根据类型拼接格式
    if (this.qrType === 'contact') {
      content = `BEGIN:VCARD\nFN:${this.contactName}\nTEL:${this.contactPhone}\nEMAIL:${this.contactEmail}\nEND:VCARD`;
    } else if (this.qrType === 'wifi') {
      content = `WIFI:T:${this.wifiEncryption};S:${this.wifiSSID};P:${this.wifiPass};;`;
    }
    this.generatedContent = content;
    // 绘制二维码(见Canvas部分)
    this.drawQRCode(content);
    // 保存历史
    this.saveHistory(content);
  }

  private async saveHistory(content: string) {
    const item: QRCodeItem = {
      id: Date.now(),
      content: content,
      type: this.qrType as any,
      createTime: Date.now(),
      color: this.qrColor,
      withLogo: this.withLogo
    };
    this.history = await qrCodeService.add(item);
  }

  // Canvas绘制二维码(模拟,实际上用矩形块表示)
  private drawQRCode(content: string) {
    // 在后续Canvas部分实现
  }

  // ... 其他方法
}

5.2 二维码生成(Canvas绘制模拟)

由于鸿蒙API 24没有内置二维码生成库,我们使用Canvas绘制一个模拟的二维码图案(用黑白方块表示),实际项目可集成第三方库。这里采用固定图案+颜色变化。

@State private canvasWidth: number = 250;
@State private canvasHeight: number = 250;
private canvasContext: CanvasRenderingContext2D | null = null;

// 在 build 中放置 Canvas
Canvas(this.canvasContext)
  .width(this.canvasWidth)
  .height(this.canvasHeight)
  .backgroundColor('#FFFFFF')
  .borderRadius(12)
  .onReady(() => {
    this.canvasContext = this.canvasContext;
    if (this.generatedContent) this.drawQRCode(this.generatedContent);
  })

private drawQRCode(content: string) {
  if (!this.canvasContext) return;
  const ctx = this.canvasContext;
  const size = this.canvasWidth;
  ctx.clearRect(0, 0, size, size);
  ctx.fillStyle = '#FFFFFF';
  ctx.fillRect(0, 0, size, size);
  
  // 根据内容生成伪随机方块(模拟二维码)
  const seed = this.hashCode(content);
  const blockSize = 8;
  const cols = Math.floor(size / blockSize);
  const rows = Math.floor(size / blockSize);
  ctx.fillStyle = this.qrColor;
  // 绘制三个定位图案(模拟)
  // 左上
  ctx.fillRect(10, 10, 30, 30);
  ctx.fillStyle = '#FFFFFF';
  ctx.fillRect(16, 16, 18, 18);
  ctx.fillStyle = this.qrColor;
  ctx.fillRect(22, 22, 6, 6);
  // 右上
  ctx.fillRect(size-40, 10, 30, 30);
  ctx.fillStyle = '#FFFFFF';
  ctx.fillRect(size-34, 16, 18, 18);
  ctx.fillStyle = this.qrColor;
  ctx.fillRect(size-28, 22, 6, 6);
  // 左下
  ctx.fillRect(10, size-40, 30, 30);
  ctx.fillStyle = '#FFFFFF';
  ctx.fillRect(16, size-34, 18, 18);
  ctx.fillStyle = this.qrColor;
  ctx.fillRect(22, size-28, 6, 6);
  
  // 随机数据区域(基于seed)
  ctx.fillStyle = this.qrColor;
  let rand = seed;
  for (let row = 0; row < rows; row++) {
    for (let col = 0; col < cols; col++) {
      // 跳过定位区域
      if ((row < 6 && col < 6) || (row < 6 && col > cols-7) || (row > rows-7 && col < 6)) continue;
      rand = (rand * 9301 + 49297) % 233280;
      if (rand % 2 === 0) {
        ctx.fillRect(col * blockSize, row * blockSize, blockSize-1, blockSize-1);
      }
    }
  }
  // 添加Logo(如果开启)
  if (this.withLogo) {
    ctx.fillStyle = '#FFFFFF';
    ctx.fillRect(size/2-20, size/2-20, 40, 40);
    ctx.fillStyle = '#1F2937';
    ctx.font = '28px sans-serif';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText('📱', size/2, size/2);
  }
}

private hashCode(str: string): number {
  let hash = 0;
  for (let i = 0; i < str.length; i++) {
    hash = (hash * 31 + str.charCodeAt(i)) & 0xFFFFFFFF;
  }
  return hash;
}

5.3 二维码美化(颜色/Logo/圆角)

已在生成中集成颜色和Logo,圆角可通过Canvas的roundRect实现(但为了简化,我们用矩形块)。

5.4 二维码保存到相册(模拟)

private async saveQRToAlbum() {
  if (!this.generatedContent) {
    prompt.showToast({ message: '请先生成二维码' });
    return;
  }
  // 模拟保存:实际需要通过 Canvas 转图片并保存到相册
  // 这里使用 Canvas 的 toDataURL 并保存(需适配)
  // 因模拟,提示成功
  prompt.showToast({ message: '已保存到相册(模拟)' });
}

5.5 二维码分享(模拟)

private shareQR() {
  if (!this.generatedContent) {
    prompt.showToast({ message: '请先生成二维码' });
    return;
  }
  // 模拟分享
  prompt.showDialog({
    title: '分享',
    message: '分享二维码(模拟)\n内容:' + this.generatedContent,
    buttons: [{ text: '确定' }]
  });
}

5.6 扫描功能(模拟+相机权限说明)

在“扫描”Tab中,显示取景框模拟,点击“开始扫描”弹出模拟结果。

@State scanResult: string = '';
@State isScanning: boolean = false;

private startScan() {
  this.isScanning = true;
  // 模拟扫描过程,2秒后返回结果
  setTimeout(() => {
    this.isScanning = false;
    // 模拟识别一个二维码内容
    this.scanResult = 'https://developer.harmonyos.com';
    prompt.showDialog({
      title: '扫描结果',
      message: this.scanResult,
      buttons: [
        { text: '复制', color: '#1F2937' },
        { text: '生成该二维码', color: '#1F2937' },
        { text: '关闭' }
      ]
    }).then(res => {
      if (res.index === 0) {
        // 复制到剪贴板
        this.copyToClipboard(this.scanResult);
      } else if (res.index === 1) {
        this.inputText = this.scanResult;
        this.qrType = 'url';
        this.currentTab = 0;
        this.generateQR();
      }
    });
  }, 2000);
}

private toggleTorch() {
  this.torchOn = !this.torchOn;
  prompt.showToast({ message: this.torchOn ? '手电筒已打开' : '手电筒已关闭' });
}

private copyToClipboard(text: string) {
  const pasteData = pasteboard.createPlainTextData(text);
  pasteboard.getSystemPasteboard().setData(pasteData, (err) => {
    if (err) prompt.showToast({ message: '复制失败' });
    else prompt.showToast({ message: '已复制到剪贴板' });
  });
}

5.7 历史记录管理(增删改查)

在“历史”Tab中,展示所有历史记录,支持点击重新生成,滑动删除。

// 历史列表
List() {
  ForEach(this.history, (item: QRCodeItem) => {
    ListItem() {
      Row() {
        Text(item.type === 'url' ? '🔗' : item.type === 'contact' ? '👤' : item.type === 'wifi' ? '📶' : '📝')
          .fontSize(24)
        Column({ space: 4 }) {
          Text(item.content.length > 20 ? item.content.slice(0,20)+'...' : item.content)
            .fontSize(14).fontColor('#1F2937')
          Text(new Date(item.createTime).toLocaleString())
            .fontSize(11).fontColor('#9CA3AF')
        }
        .margin({ left: 10 })
        .alignItems(HorizontalAlign.Start)
        .layoutWeight(1)
        Button('重新生成')
          .onClick(() => {
            this.inputText = item.content;
            this.qrType = item.type;
            this.qrColor = item.color;
            this.withLogo = item.withLogo;
            this.currentTab = 0;
            this.generateQR();
          })
          .height(28).fontSize(12).backgroundColor('#3B82F6')
      }
      .width('100%')
      .padding(12)
      .backgroundColor('#FFF')
      .borderRadius(10)
      .margin({ bottom: 8 })
    }
    .swipeAction({ end: this.DeleteAction(item.id) })
  })
}

删除动作:

@Builder DeleteAction(id: number) {
  Button('删除')
    .onClick(async () => {
      this.history = await qrCodeService.delete(id);
      prompt.showToast({ message: '已删除' });
    })
    .backgroundColor('#EF4444')
    .width(60)
    .height('100%')
}

5.8 批量生成(一次生成多个)

在“批量”Tab中,输入多行内容,生成多个二维码并展示。

@State batchInput: string = '';
@State batchResults: { content: string; color: string; withLogo: boolean }[] = [];

private batchGenerate() {
  const lines = this.batchInput.split('\n').filter(s => s.trim());
  if (lines.length === 0) {
    prompt.showToast({ message: '请输入内容' });
    return;
  }
  this.batchResults = lines.map(line => ({
    content: line.trim(),
    color: this.qrColor,
    withLogo: this.withLogo
  }));
  // 批量保存历史(为了简化,只保存第一个)
  this.saveHistory(lines[0]);
}

// 批量结果展示
Grid() {
  ForEach(this.batchResults, (item, idx) => {
    GridItem() {
      Column() {
        Text('📱').fontSize(60).backgroundColor('#FFF').padding(20).borderRadius(8)
        Text(item.content.length > 10 ? item.content.slice(0,10)+'..' : item.content)
          .fontSize(12).fontColor('#1F2937').margin({ top: 4 })
      }
      .width('100%')
      .alignItems(HorizontalAlign.Center)
      .onClick(() => {
        this.inputText = item.content;
        this.currentTab = 0;
        this.generateQR();
      })
    }
  })
}
.columnsTemplate('1fr 1fr 1fr')
.rowsGap(10)
.columnsGap(10)

5.9 名片二维码与WiFi二维码

在生成界面,选择类型为“名片”或“WiFi”时,显示对应的模板输入字段。

// 在生成界面中
if (this.qrType === 'contact') {
  Column() {
    TextInput({ placeholder: '姓名', text: this.contactName }).onChange(v => this.contactName = v).margin(4)
    TextInput({ placeholder: '电话', text: this.contactPhone }).onChange(v => this.contactPhone = v).margin(4)
    TextInput({ placeholder: '邮箱', text: this.contactEmail }).onChange(v => this.contactEmail = v).margin(4)
  }
} else if (this.qrType === 'wifi') {
  Column() {
    TextInput({ placeholder: 'WiFi名称(SSID)', text: this.wifiSSID }).onChange(v => this.wifiSSID = v).margin(4)
    TextInput({ placeholder: '密码', text: this.wifiPass }).type(InputType.Password).onChange(v => this.wifiPass = v).margin(4)
    Row() {
      Text('加密方式').width(80)
      Select([{ value: 'WPA' }, { value: 'WEP' }, { value: '无密码' }])
        .selected(0)
        .onSelect((idx) => { this.wifiEncryption = ['WPA','WEP','nopass'][idx]; })
        .width(150)
    }.margin(4)
  }
}

5.10 剪贴板识别

在生成界面添加“从剪贴板粘贴”按钮。

private pasteFromClipboard() {
  const systemPasteboard = pasteboard.getSystemPasteboard();
  systemPasteboard.getData((err, data) => {
    if (err || !data) {
      prompt.showToast({ message: '剪贴板为空或读取失败' });
      return;
    }
    const text = data.getPlainText();
    if (text) {
      this.inputText = text;
      prompt.showToast({ message: '已粘贴' });
    }
  });
}

5.11 数据持久化(Preferences)

已在 QRCodeService 中实现,每次添加或删除后自动 flush


六、UI 界面设计与实现(完整组件)

6.1 顶部标题与模式切换

@Builder TopBar() {
  Row() {
    Text('📱 二维码工具')
      .fontSize(24)
      .fontWeight(FontWeight.Bold)
      .fontColor('#1F2937')
    Blank()
    Button('清空历史')
      .fontSize(12)
      .backgroundColor('#EF4444')
      .onClick(() => {
        prompt.showDialog({
          title: '确认清空',
          message: '清空所有历史记录?',
          buttons: [{ text: '取消' }, { text: '清空', color: '#EF4444' }]
        }).then(async (res) => {
          if (res.index === 1) {
            this.history = await qrCodeService.clearAll();
            prompt.showToast({ message: '已清空' });
          }
        });
      })
  }
  .width('100%')
  .padding(16)
}

6.2 生成界面(输入/类型/颜色/Logo)

@Builder GeneratePanel() {
  Column({ space: 12 }) {
    // 类型选择
    Row() {
      Text('类型').width(60)
      Select([{ value: '网址' }, { value: '文本' }, { value: '名片' }, { value: 'WiFi' }])
        .selected(0)
        .onSelect((idx) => {
          this.qrType = ['url','text','contact','wifi'][idx];
        })
        .width(120)
    }
    // 输入框
    TextInput({ placeholder: '输入内容', text: this.inputText })
      .onChange(v => this.inputText = v)
      .width('100%')
      .height(44)
      .backgroundColor('#F3F4F6')
      .borderRadius(8)
    
    // 模板字段(略,见5.9)
    
    // 美化选项
    Row() {
      Text('颜色').width(60)
      // 简单颜色选择器(预设)
      Row({ space: 6 }) {
        ForEach(['#1F2937','#DB2777','#0891B2','#10B981','#F59E0B','#EF4444'], (c) => {
          Circle({ width: 24, height: 24 })
            .fill(c)
            .border({ width: this.qrColor === c ? 2 : 0, color: '#1F2937' })
            .onClick(() => this.qrColor = c)
        })
      }
    }
    Row() {
      Text('Logo').width(60)
      Toggle({ type: ToggleType.Switch, isOn: this.withLogo })
        .onChange(val => this.withLogo = val)
    }
    // 生成按钮
    Button('生成二维码')
      .width('100%')
      .height(48)
      .backgroundColor('#1F2937')
      .borderRadius(24)
      .onClick(() => this.generateQR())
    
    // 二维码展示(Canvas)
    Canvas(this.canvasContext)
      .width(250)
      .height(250)
      .backgroundColor('#FFF')
      .borderRadius(12)
      .margin({ top: 8 })
      .onReady(() => {
        if (this.generatedContent) this.drawQRCode(this.generatedContent);
      })
    
    // 操作按钮
    Row({ space: 12 }) {
      Button('保存').onClick(() => this.saveQRToAlbum()).backgroundColor('#10B981')
      Button('分享').onClick(() => this.shareQR()).backgroundColor('#3B82F6')
      Button('复制').onClick(() => this.copyToClipboard(this.generatedContent)).backgroundColor('#6B7280')
      Button('粘贴').onClick(() => this.pasteFromClipboard()).backgroundColor('#9CA3AF')
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceAround)
  }
  .width('90%')
  .padding(16)
}

6.3 二维码展示区(含操作按钮)

已在上面集成。

6.4 扫描界面(取景框模拟)

@Builder ScanPanel() {
  Column() {
    Stack() {
      // 模拟取景框
      Column()
        .width(250)
        .height(250)
        .border({ width: 2, color: '#1F2937', style: BorderStyle.Dashed })
        .borderRadius(12)
        .backgroundColor('rgba(255,255,255,0.1)')
      Text('📷').fontSize(80).fontColor('#FFF')
    }
    .width('100%')
    .height(300)
    .backgroundColor('rgba(0,0,0,0.7)')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
    .margin({ top: 40 })

    if (this.isScanning) {
      LoadingProgress().color('#1F2937').margin(20)
      Text('扫描中...').fontColor('#1F2937')
    } else if (this.scanResult) {
      Text(`识别结果:${this.scanResult}`).fontSize(14).margin(10)
    }

    Button('开始扫描')
      .width('60%')
      .height(48)
      .backgroundColor('#1F2937')
      .borderRadius(24)
      .margin(20)
      .onClick(() => this.startScan())

    Row() {
      Button('手电筒 ' + (this.torchOn ? '🔦' : '🔦'))
        .onClick(() => this.toggleTorch())
        .backgroundColor('#4B5563')
        .fontColor('#FFF')
    }
    .margin(10)
  }
  .width('100%')
  .height('100%')
  .backgroundColor('#F9FAFB')
}

6.5 历史列表(滑动删除)

已在5.7中实现。

6.6 批量生成与模板弹窗

@Builder BatchPanel() {
  Column() {
    Text('批量生成').fontSize(18).fontWeight(FontWeight.Medium).margin(12).alignSelf(ItemAlign.Start);
    TextArea({ placeholder: '每行一个内容', text: this.batchInput })
      .onChange(v => this.batchInput = v)
      .height(120)
      .backgroundColor('#F3F4F6')
      .borderRadius(8)
      .padding(8)
      .width('100%')
    Button('批量生成')
      .width('100%')
      .height(44)
      .backgroundColor('#1F2937')
      .borderRadius(24)
      .margin({ top: 8 })
      .onClick(() => this.batchGenerate())
    if (this.batchResults.length > 0) {
      Text(`生成 ${this.batchResults.length} 个二维码`).fontSize(14).margin(8)
      Grid() {
        ForEach(this.batchResults, (item) => {
          GridItem() { /* 显示缩略图 */ }
        })
      }
      .columnsTemplate('1fr 1fr 1fr')
      .columnsGap(8)
      .rowsGap(8)
      .width('100%')
      .height(200)
    }
  }
  .width('90%')
  .padding(16)
}

七、完整主页面代码(Index.ets)及子页面

Index.ets 集成所有功能,采用 Tabs 布局。

// Index.ets 完整骨架
@Entry
@Component
struct Index {
  // 所有状态变量
  // 所有方法

  build() {
    Column() {
      // 顶部栏
      this.TopBar();

      if (this.isLoading) {
        LoadingProgress().color('#1F2937').layoutWeight(1);
      } else {
        Tabs({ barPosition: BarPosition.End }) {
          TabContent() {
            Scroll() {
              this.GeneratePanel();
            }
            .width('100%')
            .height('100%')
            .backgroundColor('#F9FAFB')
          }
          .tabBar('生成')

          TabContent() {
            this.ScanPanel();
          }
          .tabBar('扫描')

          TabContent() {
            this.HistoryPanel();
          }
          .tabBar('历史')

          TabContent() {
            this.BatchPanel();
          }
          .tabBar('批量')
        }
        .width('100%')
        .layoutWeight(1)
        .barHeight(60)
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F9FAFB')
  }
}

八、运行与调试

8.1 环境

  • DevEco Studio 5.0+,API 24。
  • 如需真实扫描,需申请相机权限(ohos.permission.CAMERA),并集成扫码库。

8.2 运行

  1. 导入项目,含 model、service 文件。
  2. 运行模拟器,测试生成、美化、保存、历史、批量、扫描模拟等。

8.3 调试

  • 使用 HiLog 查看历史操作。
  • 测试剪贴板功能需在真机或模拟器上支持。

九、项目总结与扩展思路

9.1 项目总结

  • 功能全面:生成、美化、扫描、历史、批量、模板、剪贴板。
  • 交互流畅:Tabs切换、滑动删除、弹窗反馈。
  • 持久化:历史记录保存本地。
  • 可扩展:预留真实相机扫描、第三方二维码库集成接口。

9.2 扩展方向

  1. 真实二维码生成:集成 @ohos.qrcode 或第三方库(如QRCode npm包)。
  2. 真实扫描:使用 @ohos.multimodalInput.scanner@ohos.camera 实现相机扫码。
  3. 二维码美化模板:预设多种样式模板(渐变、圆点、边框)。
  4. 动态二维码:生成动态变化二维码。
  5. 批量导出:批量生成后一键导出所有图片。
  6. 数据加密:对内容加密后生成二维码,扫描需解密。

运行效果

二维码生成与扫描工具应用

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐