鸿蒙新特性:@ohos.pasteboard 剪贴板管理实战 — 构建剪贴板读写监听系统
引言
剪贴板是操作系统中最基础但又最常用的跨应用通信渠道。用户从浏览器复制一段文字,粘贴到聊天应用;从邮件复制一个地址,粘贴到地图搜索——每次"复制-粘贴"都涉及剪贴板的写入和读取。对于开发者来说,剪贴板 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 个交互点
- 写入剪贴板:在 TextArea 输入文本 → 点击"写入剪贴板"按钮 → 调用
createData()+setPasteData()→ 本地历史记录新增一条。 - 读取剪贴板:点击"读取剪贴板"按钮 → 调用
getDataSync()→ 在虚线框中展示文本内容 → 状态更新为"读取成功"。 - 检查内容:点击"检查内容"按钮 → 调用
hasPasteData()→ 不读取实际数据,只更新"有内容/空"标识。这个功能演示了无需 READ_PASTEBOARD 权限的操作。 - 清空剪贴板:点击"清空剪贴板"按钮 → 调用
clearData()→ 展示内容变为"(已清空)" → 状态更新。 - 变化监听:开启 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 支持分布式剪贴板——在一台设备上复制,在另一台设备上粘贴。SystemPasteboard 的 on('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 的核心用法:
- getSystemPasteboard():获取系统剪贴板单例,所有读写操作通过它进行。
- createData(mimeType, value):创建 PasteData 对象,支持纯文本、HTML、URI、PixelMap 等多种 MIME 类型。
- setPasteData() / getDataSync():异步写入和同步读取剪贴板数据。读取需要
READ_PASTEBOARD权限。 - hasPasteData() / clearData():检查是否有数据和清空剪贴板,均不需要权限。
- on(‘update’, callback):监听剪贴板变化事件,回调在内容变化时触发,配合
getDataSync()可实现自动内容感知。
剪贴板 API 的核心价值在于它打通了应用之间的数据传递——你的应用不再是一个孤岛,它可以接收来自任何应用的数据,也可以向任何应用输出数据。合理地使用剪贴板 API(特别是"一键复制"和"智能识别"两个模式),可以显著提升用户体验的流畅度。
更多推荐



所有评论(0)