第8章 HarmonyOs开发图解 剪贴板
HarmonyOS 学习系统 | 阶段二:进阶实战期 建议学习时长:1 周
学习目标
| 序号 | 能力 |
|---|---|
| 1 | 掌握 SystemPasteboard 和 PasteData 的使用方法 |
| 2 | 能实现文本复制、粘贴和剪贴板监听功能 |
| 3 | 理解剪贴板的权限限制和大小限制 |
内容讲解
8.1 剪贴板 API
社区大门口有一块便签板(系统剪贴板)。你在便签板上贴了一张写着"张三 电话 138xxxx"的便签。不管你是谁(哪个应用),只要走到便签板前(应用获得前台焦点),就能看到最新的便签内容。新的便签会覆盖旧的,便签板上的内容不会因为你离开就消失。
HarmonyOS 的 SystemPasteboard 提供了系统剪贴板操作接口,PasteData 是剪贴板数据的载体。
获取剪贴板对象
java
// 获取系统剪贴板实例
SystemPasteboard pasteboard = SystemPasteboard.getSystemPasteboard(this);
写入剪贴板
java
// 创建纯文本数据
PasteData pasteData = PasteData.createPlainTextData("Hello HarmonyOS!");
// 写入剪贴板
pasteboard.setPasteData(pasteData);
PasteData 支持多种数据类型:
| 创建方法 | 数据类型 | 说明 |
|---|---|---|
createPlainTextData() |
纯文本 | 最常用的类型 |
createHtmlData() |
HTML 文本 | 保留富文本格式 |
createUriData() |
URI 数据 | 传递链接或文件路径 |
createIntentData() |
Intent 数据 | 传递 Intent 对象 |
createMimeData() |
MIME 类型 | 自定义 MIME 类型数据 |
读取剪贴板
java
// 检查剪贴板是否有数据
if (pasteboard.hasPasteData()) {
// 获取剪贴板数据
PasteData data = pasteboard.getPasteData();
// 获取主文本
String text = data.getPrimaryText();
// 获取 HTML 文本
String html = data.getPrimaryHtml();
// 获取 URI
Uri uri = data.getPrimaryUri();
// 使用数据...
}
清除剪贴板
java
// 清除剪贴板内容
pasteboard.clearPasteData();
8.2 剪贴板变化监听
SystemPasteboard 支持监听剪贴板内容变化:
java
// 添加监听器
pasteboard.addPasteDataChangedListener(new IPasteDataChangedListener() {
@Override
public void onChanged() {
HiLog.info(TAG, "剪贴板内容发生了变化");
// 获取最新内容
if (pasteboard.hasPasteData()) {
String newText = pasteboard.getPasteData().getPrimaryText();
HiLog.info(TAG, "新内容: " + newText);
}
}
});
// 移除监听器
pasteboard.removePasteDataChangedListener(listener);
8.3 实战要点
剪贴板的权限限制
只有前台获得焦点的应用才有读取剪贴板的权限(系统默认输入法除外)。这意味着后台服务无法直接读取剪贴板内容。
剪贴板的大小限制
剪贴板单次传递内容不能超过 500KB。如果需要传递大数据量,应使用文件或分布式数据管理来实现。
剪贴板的覆盖机制
每次调用 setPasteData() 都会覆盖之前的剪贴板内容。系统剪贴板是全局唯一的,所有应用共享同一个剪贴板。
8.4 综合实战:剪贴板工具页面
下面是一个完整的剪贴板工具页面实现,包含复制、粘贴和监听功能:
java
public class ClipboardSlice extends AbilitySlice {
private static final HiLogLabel TAG = new HiLogLabel(0, 0, "ClipboardSlice");
private SystemPasteboard pasteboard;
private Text tvPasteContent;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_clipboard);
pasteboard = SystemPasteboard.getSystemPasteboard(this);
tvPasteContent = (Text) findComponentById(ResourceTable.Id_tv_paste_content);
Button btnCopy = (Button) findComponentById(ResourceTable.Id_btn_copy);
Button btnPaste = (Button) findComponentById(ResourceTable.Id_btn_paste);
Button btnClear = (Button) findComponentById(ResourceTable.Id_btn_clear);
// 复制按钮
btnCopy.setClickedListener(component -> {
TextField tfInput = (TextField) findComponentById(
ResourceTable.Id_tf_input);
String text = tfInput.getText();
if (!text.isEmpty()) {
PasteData data = PasteData.createPlainTextData(text);
pasteboard.setPasteData(data);
new ToastDialog(getContext())
.setText("已复制到剪贴板")
.show();
}
});
// 粘贴按钮
btnPaste.setClickedListener(component -> {
if (pasteboard.hasPasteData()) {
PasteData data = pasteboard.getPasteData();
String text = (data.getPrimaryText() != null)
? data.getPrimaryText().toString() : "无文本内容";
tvPasteContent.setText("剪贴板内容: " + text);
} else {
tvPasteContent.setText("剪贴板为空");
}
});
// 清除按钮
btnClear.setClickedListener(component -> {
pasteboard.clearPasteData();
tvPasteContent.setText("剪贴板已清除");
});
// 添加剪贴板变化监听
pasteboard.addPasteDataChangedListener(() -> {
getUITaskDispatcher().asyncDispatch(() -> {
if (pasteboard.hasPasteData()) {
PasteData data = pasteboard.getPasteData();
String newText = (data.getPrimaryText() != null)
? data.getPrimaryText().toString() : "非文本";
tvPasteContent.setText("监听到变化: " + newText);
}
});
});
}
@Override
public void onStop() {
// 移除监听,防止内存泄漏
pasteboard.removePasteDataChangedListener(null);
super.onStop();
}
}
8.5 剪贴板安全注意事项
- 敏感数据:不要将密码、Token 等敏感信息写入剪贴板,任何前台应用都可以读取
- 及时清除 :使用完剪贴板后,建议调用
clearPasteData()清除内容 - 监听器泄漏 :在
onStop()或onDestroy()中务必移除监听器 - 线程安全 :剪贴板变化监听回调在非 UI 线程,更新 UI 需要通过
getUITaskDispatcher()
代码速查卡
| API | 功能 | 示例 |
|---|---|---|
SystemPasteboard.getSystemPasteboard() |
获取剪贴板实例 | SystemPasteboard.getSystemPasteboard(this) |
PasteData.createPlainTextData() |
创建纯文本数据 | PasteData.createPlainTextData("text") |
setPasteData() |
写入剪贴板 | pasteboard.setPasteData(pasteData) |
getPasteData() |
读取剪贴板 | PasteData data = pasteboard.getPasteData() |
hasPasteData() |
检查是否有数据 | pasteboard.hasPasteData() |
clearPasteData() |
清除剪贴板 | pasteboard.clearPasteData() |
addPasteDataChangedListener() |
添加变化监听 | pasteboard.addPasteDataChangedListener(listener) |
removePasteDataChangedListener() |
移除变化监听 | pasteboard.removePasteDataChangedListener(listener) |
与 Android/iOS 对比
| 特性 | HarmonyOS | Android | iOS |
|---|---|---|---|
| 剪贴板管理类 | SystemPasteboard | ClipboardManager | UIPasteboard.general |
| 数据类型 | 文本/HTML/URI/Intent | 文本/URI/Intent | 文本/URL/图像/颜色 |
| 大小限制 | 500KB | 无硬性限制 | 无硬性限制 |
| 读取权限 | 前台聚焦应用 | 所有应用 | 前台应用 |
| 变化监听 | addPasteDataChangedListener | addPrimaryClipChangedListener | 无原生支持 |
| 创建数据 | PasteData | ClipData | UIPasteboard 数据 |
⚠️ 踩坑回忆录
做剪贴板功能时踩了一个大坑:我写了一个后台服务定时读取剪贴板内容,结果发现怎么都读不到。后来才知道:只有前台获得焦点的应用才有读取剪贴板的权限(系统默认输入法除外)。另外,剪贴板单次传递内容不能超过 500KB,大数据量还是得用文件或分布式数据管理。
还有一个容易忽略的问题:SystemPasteboard.getSystemPasteboard(this) 中的 this 参数是 Context,如果你在非 UI 组件中调用(比如工具类),需要传入正确的 Context,否则会返回一个无效的剪贴板实例。
必做实操任务
| 序号 | 任务 | 难度 |
|---|---|---|
| 1 | 实现一个"复制文本到剪贴板"的按钮功能 | ★☆☆ |
| 2 | 实现从剪贴板读取文本并显示的粘贴功能 | ★☆☆ |
| 3 | 添加剪贴板变化监听器,实时显示剪贴板内容变化 | ★★☆ |
| 4 | 尝试写入不同类型的数据(文本、HTML、URI)并分别读取 | ★★☆ |
| 5 | 实现一个简易的剪贴板管理工具,显示剪贴板历史记录 | ★★★ |
学习检查清单
- 能说出 SystemPasteboard 和 PasteData 的关系
- 能实现文本的复制和粘贴
- 理解剪贴板的权限限制
- 知道剪贴板的大小限制(500KB)
- 能实现剪贴板变化监听
- 了解 PasteData 支持的数据类型
- 知道什么时候应该清除剪贴板
进阶方向
- 实现剪贴板与分布式数据管理结合的跨设备剪贴板
- 了解系统输入法对剪贴板的特殊权限
- 学习在 Service 中无法读取剪贴板的替代方案