引言

剪贴板是操作系统中最基础但又最常用的跨应用通信渠道。用户从浏览器复制一段文字,粘贴到聊天应用;从邮件复制一个地址,粘贴到地图搜索——每次"复制-粘贴"都涉及剪贴板的写入和读取。对于开发者来说,剪贴板 API 的价值在于:你的应用可以主动向剪贴板写入内容(如"一键复制优惠码"),也可以读取剪贴板中的内容(如"自动识别剪贴板中的链接"),还可以监听剪贴板的变化(如"当检测到快递单号时自动弹出查询入口")。

HarmonyOS 提供了 @ohos.pasteboard 模块来实现系统剪贴板的读写操作。它通过 SystemPasteboard 对象管理剪贴板数据、PasteData 对象封装粘贴内容(支持纯文本、HTML、URI、PixelMap 等多种 MIME 类型)、on('update') 事件监听实现剪贴板变化感知。

本文将通过构建一个"剪贴板管理器",深入讲解 pasteboard 的核心 API:getSystemPasteboard()createData()setPasteData()/getDataSync()hasPasteData()clearData() 以及 on('update') 变化监听。

读完本文你将能够:

  • 使用 pasteboard.getSystemPasteboard() 获取系统剪贴板实例
  • 使用 pasteboard.createData(mimeType, value) 创建多种 MIME 类型的粘贴数据
  • 使用 setPasteData() / getDataSync() 进行剪贴板的写入和读取
  • 使用 on('update', callback) 监听剪贴板变化并自动响应
  • 了解剪贴板权限(READ_PASTEBOARD)的作用和使用场景

pasteboard 模块概述

什么是 SystemPasteboard

SystemPasteboard 是系统级剪贴板的抽象,每个应用通过 getSystemPasteboard() 获取的是同一个系统剪贴板的引用——这意味着剪贴板是跨应用共享的。你在应用中写入的数据,其他应用可以读取;其他应用复制的内容,你的应用也可以感知。

import { pasteboard } from '@kit.BasicServicesKit';

let clipboard = pasteboard.getSystemPasteboard();

// 写入
let data = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, 'Hello HarmonyOS');
clipboard.setPasteData(data);

// 读取(同步)
let pasteData = clipboard.getDataSync();
let text = pasteData.getPrimaryText();  // 'Hello HarmonyOS'

数据模型:PasteData 与 PasteDataRecord

剪贴板数据由两层结构组成:

  • PasteData:剪贴板内容的容器。一个 PasteData 可以包含多个 PasteDataRecord,每个 Record 代表一种 MIME 类型的备选数据。
  • PasteDataRecord:单个数据记录,包含 MIME 类型和对应的值。

例如,你可以创建一个 PasteData,同时包含纯文本版本(text/plain)和 HTML 版本(text/html)。当粘贴时,接收方根据自己的能力选择最合适的 MIME 类型读取。

// 创建多类型 PasteData — 同时提供纯文本和 HTML
let record = new Map<string, pasteboard.ValueType>();
record.set(pasteboard.MIMETYPE_TEXT_PLAIN, 'Hello World');
record.set(pasteboard.MIMETYPE_TEXT_HTML, '<b>Hello World</b>');
let data = pasteboard.createData(record);
clipboard.setPasteData(data);

权限说明

剪贴板 API 的权限要求按操作类型区分:

操作 权限要求 说明
setPasteData() / setData() 无需权限 写入剪贴板不需要任何权限
getDataSync() ohos.permission.READ_PASTEBOARD 同步读取需要声明权限
getData() (async) ohos.permission.READ_PASTEBOARD 异步读取也需要声明权限
hasPasteData() / hasData() 无需权限 仅检查是否有数据
clearData() 无需权限 清空剪贴板
on('update') 无需权限 监听变化不需要权限

READ_PASTEBOARD 是受保护权限(需要用户授权),对于"剪贴板管理器"这类工具型应用是合理的,但对于一般应用需要谨慎使用——用户可能会对"读取剪贴板"权限非常敏感。

核心 API 逐项解析

导入模块

import { pasteboard } from '@kit.BasicServicesKit';

模块属于 BasicServicesKit 套件,其中包含系统级基础服务。pasteboard 是一个命名空间,所有 API 都挂载在该命名空间下。

getSystemPasteboard(): SystemPasteboard

获取系统剪贴板实例。这是所有剪贴板操作的入口:

private clipboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();

通常在组件初始化时获取一次,存储为实例变量,后续所有操作通过该实例进行。SystemPasteboard 是无状态的服务代理——它不存储数据本身,只提供读写接口。

createData(mimeType, value): PasteData

创建一个包含单一记录的 PasteData 对象:

// 创建纯文本 PasteData
let data: pasteboard.PasteData = pasteboard.createData(
  pasteboard.MIMETYPE_TEXT_PLAIN,
  'Hello HarmonyOS'
);

MIME 类型常量:

  • pasteboard.MIMETYPE_TEXT_PLAIN'text/plain'
  • pasteboard.MIMETYPE_TEXT_HTML'text/html'
  • pasteboard.MIMETYPE_TEXT_URI'text/uri'
  • pasteboard.MIMETYPE_TEXT_WANT'text/want'
  • pasteboard.MIMETYPE_PIXELMAP'pixelMap'

也可以创建多记录的 PasteData——传入 Record<string, ValueType> 对象:

let multiData = pasteboard.createData({
  'text/plain': '纯文本版本',
  'text/html': '<h1>HTML 版本</h1>'
});

getDataSync(): PasteData

同步读取剪贴板内容,直接返回 PasteData 对象:

try {
  let pasteData: pasteboard.PasteData = this.clipboard.getDataSync();
  let text: string = pasteData.getPrimaryText();
  this.currentContent = text;
  this.hasContent = text.length > 0;
} catch (e) {
  this.statusMsg = '读取失败';
}

注意:同步方法 getDataSync() 需要 ohos.permission.READ_PASTEBOARD 权限。如果应用未声明该权限,运行时调用会抛出权限异常。

异步版本 getData() 返回 Promise<PasteData>,同样需要该权限:

this.clipboard.getData().then((pasteData: pasteboard.PasteData) => {
  this.currentContent = pasteData.getPrimaryText();
}).catch((err: Error) => {
  this.statusMsg = '读取失败: ' + err.message;
});

在这里插入图片描述
在这里插入图片描述

PasteData 的数据读取方法

获取到 PasteData 后,通过以下方法读取数据:

  • getPrimaryText(): string — 获取第一条 plain text 记录的内容(最常用)
  • getPrimaryMimeType(): string — 获取第一条记录的 MIME 类型
  • getRecordCount(): number — 获取记录总数
  • getRecordAt(index: number): PasteDataRecord — 获取指定索引的记录
let pasteData = clipboard.getDataSync();
if (pasteData.getRecordCount() > 0) {
  let mimeType = pasteData.getPrimaryMimeType();  // 'text/plain'
  let text = pasteData.getPrimaryText();           // 'Hello'
}

hasPasteData(): Promise<boolean>

检查剪贴板是否有数据。返回值是 Promise,不阻塞主线程,也不需要任何权限:

this.clipboard.hasPasteData().then((has: boolean) => {
  this.hasContent = has;
  this.statusMsg = has ? '剪贴板有内容' : '剪贴板为空';
});

这个方法常用于在显示"粘贴"按钮前判断剪贴板是否有可用内容。无需 READ_PASTEBOARD 权限,因为它只返回布尔值,不暴露实际数据。

clearData(): Promise<void>

清空剪贴板内容:

this.clipboard.clearData().then(() => {
  this.currentContent = '(已清空)';
  this.hasContent = false;
  this.statusMsg = '已清空剪贴板';
});

清空操作不需要任何权限。在某些场景下,用户可能希望在复制敏感信息后主动清空剪贴板(如密码管理器)。

on(‘update’, callback) 变化监听

这是 pasteboard 最有价值的 API 之一——监听剪贴板的变化:

// 开启监听
startMonitor(): void {
  this.isMonitoring = true;
  this.clipboard.on('update', () => {
    this.monitorCount++;
    this.readClipboard();  // 变化时自动读取新内容
  });
}

// 关闭监听
stopMonitor(): void {
  this.isMonitoring = false;
  this.clipboard.off('update');
}

on('update') 的回调在剪贴板内容发生变化时触发。这个变化可以来自你自己的应用(setPasteData),也可以来自其他应用(用户在其他 App 中复制了文字)。回调只通知"内容已变化",但不携带新的数据内容——你需要自己调用 getDataSync()getData() 来获取最新的内容。

注意:监听变化不需要 READ_PASTEBOARD 权限。但在回调中读取剪贴板内容时需要该权限。如果你只想知道"剪贴板是否发生了变化"而不关心具体内容,完全可以不加权限。

Demo 设计:剪贴板管理器

本文 Demo 实现了一个完整的剪贴板管理工具,包含以下功能:

页面结构

Column(根容器)
├── Header(深色标题栏:"剪贴板管理器" + @ohos.pasteboard 标签)
├── Scroll
│   └── Column
│       ├── 状态栏(监听状态 + 是否有内容 + 状态消息)
│       ├── 写入区(TextArea 输入 + "写入剪贴板" 按钮)
│       ├── 读取与操作区
│       │   ├── 当前剪贴板内容展示(虚线边框卡片)
│       │   ├── "读取剪贴板" 按钮
│       │   ├── "检查内容" 按钮
│       │   └── "清空剪贴板" 按钮
│       ├── 变化监听区(Toggle + 说明 + 变化计数)
│       ├── 写入历史区(ForEach 列表,最多 10 条)
│       └── API 参考区
└── 根容器结束

5 个交互点

  1. 写入剪贴板:在 TextArea 输入文本 → 点击"写入剪贴板"按钮 → 调用 createData() + setPasteData() → 本地历史记录新增一条。
  2. 读取剪贴板:点击"读取剪贴板"按钮 → 调用 getDataSync() → 在虚线框中展示文本内容 → 状态更新为"读取成功"。
  3. 检查内容:点击"检查内容"按钮 → 调用 hasPasteData() → 不读取实际数据,只更新"有内容/空"标识。这个功能演示了无需 READ_PASTEBOARD 权限的操作。
  4. 清空剪贴板:点击"清空剪贴板"按钮 → 调用 clearData() → 展示内容变为"(已清空)" → 状态更新。
  5. 变化监听:开启 Toggle → 调用 on('update') 注册回调 → 在另一个应用中复制文字 → 回调自动触发 → 自动读取新内容 → 变化计数 +1 → 页面实时更新。

核心实现

剪贴板实例初始化

private clipboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();

getSystemPasteboard() 在结构体初始化时调用(类字段初始化器),此时 ArkUI 组件尚未渲染,但 pasteboard 服务已经可用。

写入剪贴板

copyToClipboard(): void {
  if (this.inputText.trim().length === 0) {
    this.statusMsg = '请先输入文本';
    return;
  }
  let data: pasteboard.PasteData = pasteboard.createData(
    pasteboard.MIMETYPE_TEXT_PLAIN, this.inputText
  );
  this.clipboard.setPasteData(data).then(() => {
    this.statusMsg = '已写入剪贴板';
    let now: Date = new Date();
    let timeStr: string = this.formatTime(now);
    this.recordIndex = this.recordIndex + 1;
    let newHistory: ClipRecord[] = this.history.slice();
    newHistory.unshift(new ClipRecord(this.recordIndex,
      this.inputText.substring(0, 50), timeStr));
    if (newHistory.length > 10) {
      newHistory.pop();
    }
    this.history = newHistory;
  }).catch(() => {
    this.statusMsg = '写入失败';
  });
}

实现细节:

  • createData(MIMETYPE_TEXT_PLAIN, text) 创建纯文本粘贴数据。
  • setPasteData() 返回 Promise<void>.then() 中确认写入成功后才记录历史——如果写入失败,历史不会新增。
  • 历史记录截取前 50 个字符作为摘要,最多保留 10 条。
  • 不可变更新模式:slice() + unshift() + pop()

同步读取剪贴板

readClipboard(): void {
  try {
    let pasteData: pasteboard.PasteData = this.clipboard.getDataSync();
    let text: string = pasteData.getPrimaryText();
    if (text.length > 0) {
      this.currentContent = text;
      this.hasContent = true;
      this.statusMsg = '读取成功';
    } else {
      this.currentContent = '(剪贴板为空)';
      this.hasContent = false;
      this.statusMsg = '剪贴板为空';
    }
  } catch (e) {
    this.currentContent = '(读取失败)';
    this.statusMsg = '读取异常';
  }
}

使用 try-catch 包裹 getDataSync()——因为同步方法在以下情况会抛出异常:

  • 未声明 READ_PASTEBOARD 权限
  • 剪贴板服务不可用
  • 当前剪贴板数据格式不支持(如非文本类型的 PasteData)

变化监听生命周期

监听器的注册和注销需要正确的生命周期管理——Demo 在 aboutToDisappear() 中自动清理:

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

aboutToDisappear(): void {
  if (this.isMonitoring) {
    this.stopMonitor();
  }
}

toggleMonitor(): void {
  if (this.isMonitoring) {
    this.clipboard.off('update');
    this.isMonitoring = false;
    this.statusMsg = '监听已关闭';
  } else {
    this.clipboard.on('update', () => {
      this.monitorCount = this.monitorCount + 1;
      this.statusMsg = '检测到剪贴板变化 (#' + this.monitorCount.toString() + ')';
      this.readClipboard();
    });
    this.isMonitoring = true;
    this.statusMsg = '监听已开启';
  }
}

off('update') 用于注销之前注册的回调。如果不传第二个参数(callback),会移除该事件类型的所有已注册回调。

关键设计:回调中使用 this.readClipboard() 自动读取最新内容。这意味着用户在其他应用中复制文字后,回到本 Demo 页面时已经能看到最新内容了——状态标签也会显示"检测到剪贴板变化 #N"。

权限配置

在应用中读取剪贴板需要在 module.json5 中声明权限:

{
  "requestPermissions": [
    {
      "name": "ohos.permission.READ_PASTEBOARD",
      "reason": "$string:read_pasteboard_reason",
      "usedScene": {
        "abilities": ["EntryAbility"],
        "when": "inuse"
      }
    }
  ]
}

READ_PASTEBOARD 是 user_grant 级别的权限——首次使用时系统会弹窗请求用户授权。用户可以拒绝该权限,此时 getDataSync()getData() 将抛出权限异常。

设计产品时,建议将"读取剪贴板"作为可选功能——在无权限时,应用仍可以提供"写入剪贴板"和"检查是否有内容"的基础功能。这也是 Demo 中 checkContent() 方法的意义所在:它在无权限时也能正常工作(hasPasteData() 不需要 READ_PASTEBOARD)。

实际应用场景

场景一:一键复制

这是最常见的场景——在优惠券页面、邀请码、物流单号旁放一个"复制"按钮,用户点击后自动写入剪贴板,然后可以到任何应用粘贴。

function copyCoupon(code: string): void {
  let data = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, code);
  pasteboard.getSystemPasteboard().setPasteData(data);
  prompt.showToast({ message: '已复制到剪贴板' });
}

场景二:智能识别

结合 on('update') 监听和内容识别,实现"当用户复制了一个链接/快递单号/电话号码时,自动弹出操作入口"。这类似 iOS 的剪贴板横幅提示(Safari 检测到链接时)和 Android 的 Smart Linkify。

clipboard.on('update', () => {
  let data = clipboard.getDataSync();
  let text = data.getPrimaryText();
  if (text.startsWith('http://') || text.startsWith('https://')) {
    // 弹出"是否在浏览器中打开?"
  }
  if (/^\d{10,15}$/.test(text)) {
    // 弹出"是否查询该快递单号?"
  }
});

场景三:跨设备剪贴板

HarmonyOS 支持分布式剪贴板——在一台设备上复制,在另一台设备上粘贴。SystemPasteboardon('update') 监听也能感知远程设备带来的剪贴板变化。

clipboard.on('update', () => {
  if (clipboard.isRemoteData()) {
    let sourceDevice = clipboard.getDataSource();
    // 提示用户"来自设备 XXX 的剪贴板内容"
  }
});

场景四:安全清理

密码管理器应用在用户完成登录后,应清空剪贴板中的密码,防止被其他应用恶意读取:

// 复制密码后 30 秒自动清空
clipboard.setPasteData(data).then(() => {
  setTimeout(() => {
    clipboard.clearData();
  }, 30000);
});

总结

本文通过构建一个"剪贴板管理器",深入讲解了 HarmonyOS @ohos.pasteboard 剪贴板 API 的核心用法:

  1. getSystemPasteboard():获取系统剪贴板单例,所有读写操作通过它进行。
  2. createData(mimeType, value):创建 PasteData 对象,支持纯文本、HTML、URI、PixelMap 等多种 MIME 类型。
  3. setPasteData() / getDataSync():异步写入和同步读取剪贴板数据。读取需要 READ_PASTEBOARD 权限。
  4. hasPasteData() / clearData():检查是否有数据和清空剪贴板,均不需要权限。
  5. on(‘update’, callback):监听剪贴板变化事件,回调在内容变化时触发,配合 getDataSync() 可实现自动内容感知。

剪贴板 API 的核心价值在于它打通了应用之间的数据传递——你的应用不再是一个孤岛,它可以接收来自任何应用的数据,也可以向任何应用输出数据。合理地使用剪贴板 API(特别是"一键复制"和"智能识别"两个模式),可以显著提升用户体验的流畅度。


Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐