二维码已经成为连接物理世界和数字世界的标准方式——扫码支付、WiFi分享、名片交换、URL跳转,几乎所有"手机扫一扫"的场景背后都是二维码。ArkUI 的 QRCode 组件把二维码生成变成了真正的"一行代码":传入字符串,输出可扫的二维码。本文用它构建一个多类型二维码生成器,覆盖 URL、文本、WiFi、名片四种场景,并深入解析二维码在移动应用中的最佳实践。


一、为什么需要 QRCode 组件?

在没有 QRCode 组件之前,在应用中生成二维码有两条路:

方案一:Canvas 自绘(工作量巨大)

  1. 引入或手写 QR 编码算法(Reed-Solomon 纠错码、掩码模式选择、数据编码)
  2. 用 Canvas API 逐像素绘制 21×21 到 177×177 的矩阵
  3. 处理不同纠错级别(L/M/Q/H)对应的模块数量差异
  4. 预留静区(quiet zone)边距
  5. 确保打印/缩放后仍可扫描

这是一项需要深入理解 QR Code ISO 18004 标准的工作——随便一个实现(如 qrcodejs)源代码超过 1500 行。

方案二:第三方 JS/C++ 库
引入第三方 QR 编码库,通过 Native Binding 调用。会带来额外的包体积、兼容性风险和接口维护成本。

QRCode 组件由系统底层实现二维码编码,开发者只需要传入字符串——框架自动完成数据编码、纠错码生成、模块布局和渲染。

QRCode('https://www.harmonyos.com')
  .width(200)
  .height(200)

两行代码,一个可被任意扫码工具识别的标准二维码就完成了。


二、QRCode 核心 API

2.1 基本语法

QRCode(value: string)
  .width(length)
  .height(length)
  .backgroundColor(Color)
  • value:要编码的字符串内容。这是唯一必填的构造参数,QRCode 会将该字符串编码为二维码。
  • .width() / .height():二维码的显示尺寸。推荐设为相等值(正方形),范围建议 120~400vp。太小可能导致扫描困难,太大则超出屏幕。
  • .backgroundColor():二维码的背景色。默认为透明或白色系,可设为任意颜色。注意浅色背景 + 深色模块对比度更高,更易扫描。

2.2 内容长度与纠错

二维码有四个纠错级别,由系统根据内容长度自动选择:

纠错级别 可恢复比例 适用场景
L(低) ~7% 屏幕显示,无物理损伤风险
M(中) ~15% 一般打印用途
Q(中高) ~25% 可能被部分遮挡
H(高) ~30% 可能被大面积遮挡或损坏

屏幕上的二维码(如本文 Demo)使用 L 级别即可——没有物理折损风险。对于要在户外/包装上打印的二维码,系统会自动使用更高级别。

不同版本(模块数)对应不同容量:

  • 版本 1(21×21):最多 25 个字母数字
  • 版本 10(57×57):最多 395 个字母数字
  • 版本 40(177×177):最多 4296 个字母数字

对于 URL(~50字符)、WiFi 配置(~100字符)、名片 vCard(~200字符),二维码完全够用。

2.3 三种内容类型的编码标准

不同场景有不同的标准格式——直接传任意字符串虽然能生成二维码,但扫码端可能无法正确解析。

URL 链接

https://www.harmonyos.com

最简单的格式,直接传入完整 URL。扫码后浏览器/应用会自动打开该链接。

WiFi 网络配置

WIFI:T:WPA2;S:MyWiFi;P:12345678;;

扫码后手机会自动连接该 WiFi,无需手动输入密码。字段说明:

  • T: 加密方式(WPA2/WPA/WEP/nopass)
  • S: 网络名称(SSID)
  • P: 密码
  • H: 隐藏网络(true/false,可选)

名片 vCard

BEGIN:VCARD
VERSION:3.0
N:张三
TEL:13800138000
EMAIL:zhangsan@example.com
ORG:华为技术有限公司
END:VCARD

扫码后手机自动弹出"添加联系人"界面,姓名/电话/邮箱/公司已预填。这是商务场景中最常用的二维码格式。

2.4 二维码的视觉安全

二维码模块的标准颜色是黑色。但出于品牌表达或美观需要,可以自定义颜色。要注意:

  • 深色模块 + 浅色背景:可扫描性最好
  • 彩色模块:只要与背景有足够对比度即可
  • 反色(白模块+黑背景):多数扫码器支持
  • 避免:浅色模块 + 浅色背景、渐变填充、低对比度

2.5 最小尺寸建议

二维码的物理尺寸直接影响扫描距离和成功率:

屏幕场景 推荐尺寸 扫描距离
App 内的电子二维码 160×160 ~ 200×200 vp 10~30cm
收银台/闸机屏幕 240×240 ~ 320×320 vp 20~50cm
大屏/电视 300×300 ~ 400×400 vp 1~3m

太小(<120vp)会导致手机摄像头无法聚焦,尤其在中低端设备上扫描成功率急剧下降。


在这里插入图片描述

三、Demo:多类型二维码生成器

本 Demo 构建一个可切换内容类型的二维码生成器——支持 URL 链接、文本、WiFi 配置、名片四种场景,每种场景有不同的输入字段,输入即生成。

页面结构

QRCodeDemoPage (~270行)
├── Header(📱 QR码生成器 + 内容字符数)
├── 类型选择栏(链接/文本/WiFi/名片,4个Tab按钮)
├── Scroll
│   ├── 输入区域(白卡片)
│   │   ├── 链接模式:TextInput(单行URL输入)
│   │   ├── 文本模式:TextArea(多行自由文本)
│   │   ├── WiFi模式:SSID + 密码 + 加密方式选择
│   │   └── 名片模式:姓名 + 电话 + 邮箱 + 公司
│   └── 预览区域(白卡片)
│       ├── 尺寸控制(- / Nx / +)
│       ├── QRCode 组件
│       └── 当前内容描述

类型切换设计

四个类型使用不同颜色区分,选中态为填充色按钮,非选中态为灰色轮廓:

enum QRType {
  URL = '链接',
  TEXT = '文本',
  WIFI = 'WiFi',
  CONTACT = '名片'
}

getTypeColor(type: QRType): string {
  switch (type) {
    case QRType.URL: return '#1677FF';     // 蓝
    case QRType.TEXT: return '#52C41A';    // 绿
    case QRType.WIFI: return '#722ED1';    // 紫
    case QRType.CONTACT: return '#E67E22'; // 橙
  }
}

切换类型时,自动根据当前输入更新二维码内容(selectedType 变化 → updateQRContent() → 重新生成对应的标准格式字符串)。

四种输入界面

链接模式

最简单的模式——单行 TextInput,预填 https://www.harmonyos.com。用户输入时实时更新二维码。

if (this.selectedType === QRType.URL) {
  TextInput({ text: this.urlInput, placeholder: '请输入URL链接地址' })
    .width('100%').height(44)
    .onChange((value: string) => {
      this.urlInput = value;
      this.updateQRContent();
    })
}
文本模式

多行 TextArea,支持任意文本。适合生成长段文字二维码(如便签、说明、诗歌)。预填"你好,鸿蒙!Hello HarmonyOS!"。

if (this.selectedType === QRType.TEXT) {
  TextArea({ text: this.textInput, placeholder: '请输入任意文本内容' })
    .width('100%').height(100)
    .onChange((value: string) => {
      this.textInput = value;
      this.updateQRContent();
    })
}
WiFi 模式

三个配置项:网络名称(SSID)、密码、加密方式。使用 WIFI 标准格式拼接:

this.qrContent = `WIFI:T:${this.wifiEncryption};S:${this.wifiSsid};P:${this.wifiPassword};;`;

加密方式提供四个选项:WPA2(默认)、WPA、WEP、无(开放网络)。这在咖啡馆、酒店等场景非常有用——扫码即可连 WiFi,不需要手动输入密码。

名片模式

四个字段:姓名、电话、邮箱、公司。使用 vCard 3.0 格式编码:

this.qrContent =
  `BEGIN:VCARD\nVERSION:3.0\nN:${this.contactName}\nTEL:${this.contactPhone}\n` +
  `EMAIL:${this.contactEmail}\nORG:${this.contactCompany}\nEND:VCARD`;

扫码后手机通讯录自动识别并弹出添加联系人界面。商务会议中这个场景极其常见——不需要交换纸质名片,扫一扫即可。

尺寸控制

两个圆形按钮(- / +)让用户调整二维码尺寸,范围 120~320vp,每次步进 40vp:

.onClick(() => {
  if (this.qrSize > 120) {
    this.qrSize -= 40;
  }
})

实时显示当前缩放倍率(如 “5x”、“6x”),给用户直观的尺寸反馈。

三个交互点

  1. 类型切换 — 点击四个类型 Tab,输入区自动切换对应表单,QRCode 实时更新
  2. 输入即生成 — 任何字段内容变化时,二维码自动重新编码
  3. 尺寸缩放 — 点击 +/- 按钮调整二维码显示大小(120~320vp)

在这里插入图片描述

四、完整代码

import { AppColors, BorderRadius, FontSize, Spacing } from '../common/Constants';
import { promptAction } from '@kit.ArkUI';

enum QRType {
  URL = '链接',
  TEXT = '文本',
  WIFI = 'WiFi',
  CONTACT = '名片'
}

interface QRField {
  label: string;
  placeholder: string;
  value: string;
}

@Entry
@Component
struct QRCodeDemoPage {
  @State selectedType: QRType = QRType.URL;
  @State qrContent: string = 'https://www.harmonyos.com';
  @State qrSize: number = 200;
  @State urlInput: string = 'https://www.harmonyos.com';
  @State textInput: string = '你好,鸿蒙!Hello HarmonyOS!';
  @State wifiSsid: string = 'MyWiFi';
  @State wifiPassword: string = '12345678';
  @State wifiEncryption: string = 'WPA2';
  @State contactName: string = '张三';
  @State contactPhone: string = '13800138000';
  @State contactEmail: string = 'zhangsan@example.com';
  @State contactCompany: string = '华为技术有限公司';

  aboutToAppear(): void {
    this.updateQRContent();
  }

  updateQRContent(): void {
    switch (this.selectedType) {
      case QRType.URL:
        this.qrContent = this.urlInput.trim() || 'https://www.harmonyos.com';
        break;
      case QRType.TEXT:
        this.qrContent = this.textInput.trim() || 'Hello HarmonyOS';
        break;
      case QRType.WIFI:
        this.qrContent =
        `WIFI:T:${this.wifiEncryption};S:${this.wifiSsid};P:${this.wifiPassword};;`;
        break;
      case QRType.CONTACT:
        this.qrContent =
        `BEGIN:VCARD\nVERSION:3.0\nN:${this.contactName}\nTEL:${this.contactPhone}\nEMAIL:${this.contactEmail}\nORG:${this.contactCompany}\nEND:VCARD`;
        break;
    }
  }

  getTypeColor(type: QRType): string {
    switch (type) {
      case QRType.URL: return '#1677FF';
      case QRType.TEXT: return '#52C41A';
      case QRType.WIFI: return '#722ED1';
      case QRType.CONTACT: return '#E67E22';
    }
  }

  build() {
    Column() {
      Row() {
        Text('📱 QR码生成器')
          .fontSize(FontSize.HEADLINE)
          .fontWeight(FontWeight.Bold)
          .fontColor(Color.White)
          .layoutWeight(1)
        Text(`${this.qrContent.length}字符`)
          .fontSize(10)
          .fontColor('#FFFFFF88')
      }
      .width('100%')
      .height(52)
      .backgroundColor('#1a1a2e')
      .padding({ left: Spacing.XXL, right: Spacing.XXL })

      Row() {
        ForEach([QRType.URL, QRType.TEXT, QRType.WIFI, QRType.CONTACT],
        (type: QRType) => {
          Text(type)
            .fontSize(FontSize.CAPTION)
            .fontColor(this.selectedType === type ? Color.White :
            AppColors.TEXT_SECONDARY)
            .fontWeight(this.selectedType === type ? FontWeight.Medium :
            FontWeight.Regular)
            .padding({ left: 14, right: 14, top: 6, bottom: 6 })
            .borderRadius(9999)
            .backgroundColor(this.selectedType === type ?
              this.getTypeColor(type) : '#F0F0F0')
            .margin({ right: Spacing.SM })
            .onClick(() => {
              this.selectedType = type;
              this.updateQRContent();
            })
        })
      }
      .width('100%')
      .padding({ left: Spacing.LG, right: Spacing.LG, top: Spacing.MD,
        bottom: Spacing.MD })
      .backgroundColor(Color.White)
      .border({ width: { bottom: 1 }, color: '#F0F0F0' })

      Scroll() {
        Column() {
          Column() {
            Row() {
              Text('输入内容')
                .fontSize(FontSize.CAPTION)
                .fontColor(AppColors.TEXT_TERTIARY)
                .margin({ bottom: Spacing.SM })
            }
            .width('100%')

            if (this.selectedType === QRType.URL) {
              TextInput({ text: this.urlInput, placeholder: '请输入URL链接地址' })
                .fontSize(FontSize.BODY)
                .width('100%')
                .height(44)
                .borderRadius(8)
                .backgroundColor('#F5F6FA')
                .padding({ left: 12, right: 12 })
                .onChange((value: string) => {
                  this.urlInput = value;
                  this.updateQRContent();
                })
            }

            if (this.selectedType === QRType.TEXT) {
              TextArea({ text: this.textInput, placeholder: '请输入任意文本内容' })
                .fontSize(FontSize.BODY)
                .width('100%')
                .height(100)
                .borderRadius(8)
                .backgroundColor('#F5F6FA')
                .padding({ left: 12, right: 12, top: 10, bottom: 10 })
                .onChange((value: string) => {
                  this.textInput = value;
                  this.updateQRContent();
                })
            }

            if (this.selectedType === QRType.WIFI) {
              Column() {
                Row() {
                  Text('网络名称')
                    .fontSize(FontSize.CAPTION)
                    .fontColor(AppColors.TEXT_SECONDARY)
                    .width(60)
                  TextInput({ text: this.wifiSsid, placeholder: 'WiFi名称' })
                    .fontSize(FontSize.BODY)
                    .layoutWeight(1)
                    .height(40)
                    .borderRadius(8)
                    .backgroundColor('#F5F6FA')
                    .padding({ left: 12, right: 12 })
                    .onChange((v: string) => { this.wifiSsid = v;
                      this.updateQRContent(); })
                }
                .width('100%')
                .margin({ bottom: Spacing.SM })

                Row() {
                  Text('密码')
                    .fontSize(FontSize.CAPTION)
                    .fontColor(AppColors.TEXT_SECONDARY)
                    .width(60)
                  TextInput({ text: this.wifiPassword, placeholder: 'WiFi密码' })
                    .fontSize(FontSize.BODY)
                    .layoutWeight(1)
                    .height(40)
                    .borderRadius(8)
                    .backgroundColor('#F5F6FA')
                    .padding({ left: 12, right: 12 })
                    .type(InputType.Password)
                    .onChange((v: string) => { this.wifiPassword = v;
                      this.updateQRContent(); })
                }
                .width('100%')
                .margin({ bottom: Spacing.SM })

                Row() {
                  Text('加密方式')
                    .fontSize(FontSize.CAPTION)
                    .fontColor(AppColors.TEXT_SECONDARY)
                    .width(60)
                  Row() {
                    ForEach(['WPA2', 'WPA', 'WEP', '无'],
                    (enc: string) => {
                      Text(enc)
                        .fontSize(11)
                        .fontColor(this.wifiEncryption === enc ? Color.White :
                        AppColors.TEXT_SECONDARY)
                        .padding({ left: 10, right: 10, top: 4, bottom: 4 })
                        .borderRadius(9999)
                        .backgroundColor(this.wifiEncryption === enc ?
                        '#722ED1' : '#F0F0F0')
                        .margin({ right: 6 })
                        .onClick(() => {
                          this.wifiEncryption = enc;
                          this.updateQRContent();
                        })
                    })
                  }
                }
                .width('100%')
              }
              .width('100%')
            }

            if (this.selectedType === QRType.CONTACT) {
              Column() {
                this.ContactField('姓名', this.contactName,
                '请输入姓名', (v: string) => { this.contactName = v;
                  this.updateQRContent(); })
                this.ContactField('电话', this.contactPhone,
                '请输入电话号码', (v: string) => { this.contactPhone = v;
                  this.updateQRContent(); })
                this.ContactField('邮箱', this.contactEmail,
                '请输入邮箱地址', (v: string) => { this.contactEmail = v;
                  this.updateQRContent(); })
                this.ContactField('公司', this.contactCompany,
                '请输入公司名称', (v: string) => { this.contactCompany = v;
                  this.updateQRContent(); })
              }
              .width('100%')
            }
          }
          .width('100%')
          .padding(Spacing.LG)
          .backgroundColor(Color.White)
          .borderRadius(BorderRadius.MD)
          .margin({ left: Spacing.LG, right: Spacing.LG, top: Spacing.LG })

          Column() {
            Row() {
              Text('预览')
                .fontSize(FontSize.CAPTION)
                .fontColor(AppColors.TEXT_TERTIARY)
              Blank()
              Row() {
                Text('-')
                  .fontSize(FontSize.TITLE)
                  .fontColor(AppColors.PRIMARY)
                  .width(32).height(32)
                  .borderRadius(16)
                  .backgroundColor('#F0F0F0')
                  .textAlign(TextAlign.Center)
                  .onClick(() => {
                    if (this.qrSize > 120) {
                      this.qrSize -= 40;
                    }
                  })
                Text(`${(this.qrSize / 40).toFixed(0)}x`)
                  .fontSize(11)
                  .fontColor(AppColors.TEXT_SECONDARY)
                  .margin({ left: 8, right: 8 })
                Text('+')
                  .fontSize(FontSize.TITLE)
                  .fontColor(AppColors.PRIMARY)
                  .width(32).height(32)
                  .borderRadius(16)
                  .backgroundColor('#F0F0F0')
                  .textAlign(TextAlign.Center)
                  .onClick(() => {
                    if (this.qrSize < 320) {
                      this.qrSize += 40;
                    }
                  })
              }
            }
            .width('100%')
            .margin({ bottom: Spacing.LG })

            QRCode(this.qrContent)
              .width(this.qrSize)
              .height(this.qrSize)
              .backgroundColor(Color.White)

            Text(this.getQRDescription())
              .fontSize(FontSize.CAPTION)
              .fontColor(AppColors.TEXT_TERTIARY)
              .margin({ top: Spacing.LG })
              .maxLines(2)
              .textOverflow({ overflow: TextOverflow.Ellipsis })
          }
          .width('100%')
          .padding(Spacing.XXL)
          .backgroundColor(Color.White)
          .borderRadius(BorderRadius.MD)
          .margin({ left: Spacing.LG, right: Spacing.LG, top: Spacing.LG,
            bottom: Spacing.XXL })
          .alignItems(HorizontalAlign.Center)
        }
        .width('100%')
      }
      .layoutWeight(1)
      .scrollBar(BarState.Off)
      .backgroundColor('#F5F6FA')
    }
    .width('100%')
    .height('100%')
  }

  getQRDescription(): string {
    switch (this.selectedType) {
      case QRType.URL:
        return `链接:${this.urlInput.trim() || '(空)'}`;
      case QRType.TEXT:
        const t = this.textInput.trim();
        return t ? `文本内容 (${t.length}字符)` : '(空)';
      case QRType.WIFI:
        return `WiFi: ${this.wifiSsid} | ${this.wifiEncryption}`;
      case QRType.CONTACT:
        return `名片: ${this.contactName} | ${this.contactPhone}`;
    }
  }

  @Builder
  ContactField(label: string, value: string, placeholder: string,
  onChange: (v: string) => void) {
    Row() {
      Text(label)
        .fontSize(FontSize.CAPTION)
        .fontColor(AppColors.TEXT_SECONDARY)
        .width(40)
      TextInput({ text: value, placeholder: placeholder })
        .fontSize(FontSize.BODY)
        .layoutWeight(1)
        .height(40)
        .borderRadius(8)
        .backgroundColor('#F5F6FA')
        .padding({ left: 12, right: 12 })
        .onChange(onChange)
    }
    .width('100%')
    .margin({ bottom: Spacing.SM })
  }
}

五、二维码的实际应用架构

5.1 App 内展示 + 系统扫码

这是最常见的使用模式:

[App 页面] → QRCode 组件(展示)
                ↓ 用户拿另一部手机
            [系统相机/扫一扫] → 解析 → 跳转/连接/添加联系人

代码侧只需要 QRCode(value)。剩下的全部由手机系统(相机扫码识别)完成。这是"零后端"的场景——二维码是纯客户端技术。

5.2 App 内扫码 + App 内展示

在同一个 App 内完成"生成"和"扫描"的闭环:

// 页面 A:生成二维码
QRCode(this.generateTicketCode())

// 页面 B:扫描二维码(使用 @ohos.scan 扫码能力)
// 扫码完成后在 App 内处理

这种模式常用于:

  • 电子票务(电影院 App → 生成门票二维码 → 检票端扫描)
  • 共享设备(设备屏幕显示二维码 → App 扫码绑定)
  • 身份验证(App 生成身份码 → 另一端扫码认证)
  • 文件传输(发送端生成码 → 接收端扫码下载)

5.3 动态二维码 vs 静态二维码

静态二维码 动态二维码
编码内容 直接嵌入信息(如 URL) 嵌入短链 ID,指向后端服务
能否修改 生成后不可改 后端修改跳转目标即可
需要后端
典型场景 WiFi 分享、名片 产品追溯、营销活动
容量需求 较大 较小(仅需 ID)

本文 Demo 的四类二维码都是静态二维码——信息直接编码在二维码图案中,不依赖网络或后端服务。扫码端离线也能正常解析。


六、常见面试题 / 踩坑点

6.1 QRCode 的内容长度有限制吗?

有。不同纠错级别和版本的容量不同。作为参考:

  • 纯数字:最多 7089 个字符
  • 字母数字:最多 4296 个字符
  • 二进制(8-bit):最多 2953 个字符
  • 中文(UTF-8):最多 ~1800 个字符

如果内容过长,QRCode 组件会自动升级到更高版本来容纳。但注意:版本越高,模块越多,图案越密集,需要的显示尺寸也越大。对于超过 500 字符的内容,建议使用至少 200×200vp 的尺寸。

6.2 为什么我生成的 WiFi 二维码扫了无法连接?

WiFi 格式有严格规范,检查以下几点:

  1. 字段分隔符必须是分号 ;
  2. 末尾必须有 ;;(两个分号,最后一个空字段)
  3. T: 字段值必须是 WPA2WPAWEPnopass 之一
  4. 如果密码包含特殊字符(如 ;:\),需要转义:\;\:\\
// 正确
'WIFI:T:WPA2;S:MyWiFi;P:12345678;;'

// 错误 — 末尾缺少 ;;
'WIFI:T:WPA2;S:MyWiFi;P:12345678;'

// 错误 — 密码中包含未转义的分号
'WIFI:T:WPA2;S:MyWiFi;P:abc;123;;'

6.3 二维码尺寸该设多大?

参见上文表格。一个简单规则:最小 150vp,推荐 200vp,不要超过屏幕宽度的 70%。

在本文 Demo 中,默认尺寸为 200vp,提供 120~320vp 的调整范围。用户可以按 + / - 按钮缩放,步长 40vp。

6.4 可以给二维码加 Logo 吗?

QRCode 组件本身不支持叠加 Logo。如需"带 Logo 的二维码",有两种方案:

  1. 在 QRCode 组件上方叠加一个 Stack 中的 Image,放在中心位置(利用 QR 码的纠错能力,中心区域的图案被遮挡不超过 30% 仍可扫描)
  2. 使用 Canvas 自绘整个二维码 + Logo

方案 1 是最常见的做法——微信、支付宝的收款码都是二维码 + 中心 Logo 的设计。

Stack() {
  QRCode(this.content)
    .width(200).height(200)
  Image($r('app.media.logo'))
    .width(40).height(40)
    .borderRadius(8)
}

6.5 生成的二维码在深色模式(Dark Mode)下还能扫吗?

默认情况下 QRCode 生成黑色模块 + 透明/白色背景。如果 App 开启了深色模式且整体背景变黑,二维码依然是黑模块 + 透明背景——因为 QRCode 的默认背景色不跟随系统主题变化。

但如果你手动设置了 .backgroundColor(Color.Black),那二维码就"消失"了(黑模块 + 黑背景 = 全黑)。建议始终为 QRCode 显式设置白色背景:

QRCode(this.content)
  .backgroundColor(Color.White)

七、总结

QRCode 是 ArkUI 中"从不可能到可能"的组件——与 RatingRefresh 这类"从手动实现到声明式配置"的组件不同,二维码生成的底层算法复杂度决定了,没有 QRCode 组件就意味着要么引入第三方库,要么放弃在 App 中生成二维码的能力。系统内置 = 零依赖 + 零体积开销 + 与系统扫码体验一致。

1. 一行代码,消灭 1500 行 QR 编码算法。 不需要理解 Reed-Solomon 纠错码、掩码模式、数据位布局——QRCode(value) 就完成了从编码到渲染的全部流程。对于开发者来说,这个组件的存在本身就是最大价值。

2. 内容标准化比组件本身更重要。 WiFi 格式(WIFI:T:...;S:...;P:...;;)、vCard 格式(BEGIN:VCARD...)——这些标准决定了"扫出来是什么效果"。组件负责生成图案,开发者负责生成正确的字符串。

3. 四个参数覆盖 90% 场景。 value(内容)是必填的唯一参数,.width().height() 控制尺寸,.backgroundColor() 确保对比度。没有多余的 API——因为它们确实不需要。

4. 二维码是"离线"的。 与 API 调用、数据库查询、网络请求不同,二维码编码完全在本地完成——不需要网络、不需要后端、不需要权限。这是它最独特的优势:在完全离线的情况下,仍然能完成信息传递。

QRCode 最合适的应用场景:

  • WiFi 分享 / 设备配网(扫码连接,不用输密码)
  • 电子名片 / 商务社交(扫码添加联系人)
  • URL 跳转 / 应用推广(扫码打开链接)
  • 电子票务 / 核销码(扫码验证身份)
  • 任何"手机扫一下就能完成操作"的交互

在移动端,二维码已经从"小众技术"变成了"基础设施"。QRCode 组件让这个基础设施可以被任何 ArkUI 开发者免费使用——这是 HarmonyOS 平台能力下沉的又一个典型案例。

Logo

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

更多推荐