在鸿蒙应用里加一个“一键复制”功能,看起来只是调用一个API的事,但实际做下来才发现,读取剪贴板需要受限权限,权限还得去AGC申请Profile,中间有不少坑。这篇文章基于ASCF框架的剪贴板API,把写入、读取、权限申请、版本兼容和组合玩法完整梳理一遍。
ASCF框架提供的剪贴板API并不复杂,总共两个:setClipboardData用于写入,getClipboardData用于读取。其中setClipboardData从ASCF 1.0.8版本开始提供,而getClipboardData是1.0.21才加入的。如果项目还跑在1.0.21以下的老版本上,读取功能会直接不可用,需要做兼容处理。
写入剪贴板:setClipboardData
setClipboardData的用法很直接,传给data一个字符串即可。调用成功后,系统会自动弹出“内容已复制”的提示,不需要开发者自己写toast。三个回调success、fail、complete和其他ASCF API风格保持一致。
- has.setClipboardData({
- data: '要复制的内容',
- success: () => {
- console.info('复制成功');
- },
- fail: (err) => {
- console.error('复制失败:', err);
- },
- complete: () => {
- console.info('setClipboardData 调用完成');
- }
- });
复制代码
注意success回调里没有任何参数,复制操作本身就是“写入后不管”的语义。另外data参数只能传字符串,如果想复制结构化数据,需要先JSON.stringify再传进去,读取时再JSON.parse解析回来。
实际项目中一个典型的复制订单号场景:
- copyOrderNo() {
- let that = this;
- has.setClipboardData({
- data: that.data.orderNo,
- success: () => {
- console.info('订单号已复制');
- },
- fail: (err) => {
- has.showModal({
- title: '复制失败',
- content: JSON.stringify(err),
- confirmColor: '#0A59F7',
- showCancel: false,
- });
- }
- });
- }
复制代码
模板里只需一个按钮绑定事件。复制链接同理,只是文本内容变成了拼接后的URL。如果要做分享功能,注意剪贴板API无法复制图片或文件,只能复制纯文本。
读取剪贴板:getClipboardData与权限申请
读取比写入麻烦很多。getClipboardData需要一个特殊权限:ohos.permission.READ_PASTEBOARD。这个权限属于受限权限,并不是在module.json5里声明一下就能用,还需要去AppGallery Connect(AGC)申请Profile。
第一步,在module.json5中声明权限:
- {
- "module": {
- "requestPermissions": [
- {
- "name": "ohos.permission.READ_PASTEBOARD"
- }
- ]
- }
- }
复制代码
第二步,登录AppGallery Connect,找到应用,在“证书、APP ID和Profile”中申请对应Profile。申请时需要说明读取剪贴板的用途,比如“用于用户主动触发的粘贴操作”。审批通过后下载Profile文件,替换项目里的旧文件。
如果只声明权限而没去AGC申请Profile,调用getClipboardData会一直走fail回调,而且错误码不一定直观,排查起来很费劲。这一点是最容易踩的坑。
权限搞定后,基本用法如下:
- has.getClipboardData({
- success: (res) => {
- console.info('剪贴板内容:', res.data);
- },
- fail: (err) => {
- console.error('读取失败:', err);
- },
- complete: () => {
- console.info('getClipboardData 调用完成');
- }
- });
复制代码
success回调的res.data是字符串类型,表示当前剪贴板内容。如果剪贴板为空,res.data是空字符串。
一个常见的读取场景是粘贴验证码。用户点击“粘贴”按钮后,从剪贴板读取内容并提取数字验证码。这里强调一点:不要页面一打开就自动读取剪贴板,一定要用户主动触发,避免隐私合规风险。也可以配合系统在状态栏提示“应用读取了剪贴板”,体验上要谨慎设计。
- pasteCode() {
- let that = this;
- has.getClipboardData({
- success: (res) => {
- if (res.data) {
- let code = res.data.replace(/\D/g, '');
- if (code.length >= 4 && code.length <= 6) {
- that.setData({ codeInput: code });
- } else {
- that.setData({ codeInput: res.data });
- }
- }
- },
- fail: (err) => {
- console.error('读取剪贴板失败:', err);
- }
- });
- }
复制代码
实用Demo:复制+读取完整页面
为了方便验证功能,可以做一个完整页面,包含输入框、复制按钮、读取按钮、常用文本快捷复制和事件日志。页面结构主要分三个区域:输入操作区、常用文本区、事件日志区。输入区支持自定义内容复制和剪贴板读取;常用文本区维护一个文本列表,每个条目带一个复制按钮;日志区记录每次操作的时间与结果,方便调试。
快捷复制的核心思路是维护一个quickTexts数组,通过data-index传递索引,点击对应按钮就复制对应文本。
- data: {
- quickTexts: [
- { text: 'https://developer.huawei.com' },
- { text: 'support@huawei.com' },
- { text: '鸿蒙开发真好用' },
- { text: 'Hello HarmonyOS!' },
- { text: '192.168.1.100' }
- ]
- },
- quickCopy(e) {
- let index = e.currentTarget.dataset.index;
- let item = this.data.quickTexts[index];
- has.setClipboardData({
- data: item.text,
- success: () => {
- console.info('已复制:', item.text);
- }
- });
- }
复制代码
如果想更灵活,可以把quickTexts存到本地缓存里,让用户自己管理常用文本,这就是storage加clipboard两个API的组合。
踩坑记录与注意事项
1. READ_PASTEBOARD权限不是声明了就能用。module.json5声明与AGC申请Profile缺一不可。调用getClipboardData失败时,错误信息可能不直观,建议在fail回调中给用户明确提示,而不是只打console.error。
2. setClipboardData的data只支持字符串。复制JSON对象等结构化数据时,先JSON.stringify,读取时再JSON.parse,同时做好解析异常处理,避免剪贴板内容不是JSON时崩溃。
- let userInfo = { name: '张三', phone: '13800138000' };
- has.setClipboardData({
- data: JSON.stringify(userInfo),
- });
- has.getClipboardData({
- success: (res) => {
- try {
- let info = JSON.parse(res.data);
- console.info('用户名:', info.name);
- } catch (e) {
- console.info('普通文本:', res.data);
- }
- }
- });
复制代码
3. 系统弹窗无法自定义。setClipboardData成功后自动弹出的“内容已复制”提示,无法去掉,也无法修改文案。UI设计时不要把这个弹窗样式写死,不同鸿蒙版本的系统提示样式可能不一样。
4. 剪贴板是系统级共享的,其他应用可以覆盖内容。不要把剪贴板当存储用,复制完需要保存的数据要及时写入本地缓存。
5. 隐私合规问题。读取剪贴板操作敏感,系统可能提示“XX应用读取了剪贴板”。务必让用户主动触发读取行为,避免在后台自动读取,否则体验差且审核有风险。
版本兼容处理
由于getClipboardData在ASCF 1.0.21才加入,如果应用需要兼容低版本,可以判断API是否存在:
- if (typeof has.getClipboardData === 'function') {
- has.getClipboardData({
- success: (res) => {
- console.info('剪贴板内容:', res.data);
- },
- fail: (err) => {
- console.error('读取失败:', err);
- }
- });
- } else {
- has.showModal({
- title: '提示',
- content: '当前版本不支持读取剪贴板,请升级应用',
- confirmColor: '#0A59F7',
- showCancel: false,
- });
- }
复制代码
当前大部分设备的ASCF版本应该都在1.0.21以上,但加上兼容处理总归更稳妥。
剪贴板与其他API的组合玩法
剪贴板API虽然简单,组合起来能覆盖不少场景。例如分享时先复制内容到剪贴板,用户分享失败还能手动粘贴;笔记类应用可以结合本地缓存实现“复制历史”功能,最多保留20条记录;聊天或表单场景可以配合输入框实现“粘贴”按钮,把剪贴板内容追加到输入框中。
复制历史的实现也很直接:
- copyWithHistory(text) {
- let that = this;
- has.setClipboardData({
- data: text,
- success: () => {
- let history = [];
- try {
- history = JSON.parse(has.getStorageSync('copyHistory') || '[]');
- } catch (e) {
- history = [];
- }
- history.unshift({
- text: text,
- time: new Date().toLocaleString(),
- });
- if (history.length > 20) {
- history = history.slice(0, 20);
- }
- has.setStorageSync('copyHistory', JSON.stringify(history));
- }
- });
- }
复制代码
调试建议:在Demo页面加一个事件日志区域,每次API调用都记录时间、成功或失败信息,日志最多保留15条,最新在最上面。这比只看console输出直观得多,尤其是在真机上排查权限问题时能快速定位是权限没申请、API版本不支持还是其他原因。
总结一下:ASCF剪贴板API就两个方法,写入简单,但读取需要走权限申请流程。实际项目里复制订单号、复制链接、复制验证码、分享文案都是高频场景。写入时记住系统自动弹提示,读取时务必处理好权限异常和版本兼容,同时注意隐私合规,让用户主动触发读取。如果应用有一键复制需求,建议把fail回调也处理好,系统剪贴板服务万一出问题,给用户一个友好提示总比静默失败强。 |