
想了解更多内容,分布请访问:
和华为官方合作共建的式粘鸿蒙技术社区
https://harmonyos.51cto.com
一、介绍
HarmonyOS提供系统剪贴板服务的贴板操作接口,支持用户程序从系统剪贴板中读取、分布写入和查询剪贴板数据,式粘以及添加、贴板移除系统剪贴板数据变化的分布回调。
设备内:
用户通过系统剪贴板服务,式粘可实现应用之间的贴板简单数据传递。例如:在应用A中复制的分布数据,可以在应用B中粘贴,式粘反之亦可。贴板
设备间:
在分布式粘贴板场景中,分布粘贴的式粘数据可以跨设备写入。例如,贴板设备A上的应用程序使用系统粘贴板接口将从设备A复制的数据通过IDL接口存储到设备B的系统粘贴板中。如果数据允许,设备B上的应用程序可以读取并粘贴系统粘贴板中的复制数据。实现设备之间粘贴板的分布式协同。
基于以上理解,实现一个分布式粘贴板应用程序,应用程序分为客户端(copy)和服务端(paste)两部分,云服务器提供商通过idl实现数据传递。
客户端负责数据采集,服务端负责数据的展示和应用,客户端和服务端可以安装在同一台设备中,也可以安装在不同的设备中,服务端也可以按照在多台设备中,服务端通过分布式数据库实现粘贴板数据的自动同步。
二、效果展示


三、搭建环境
安装DevEco Studio,详情请参考DevEco Studio下载。
设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:
如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。
下载源码后,使用DevEco Studio 打开项目,模拟器运行即可。
真机运行需要将config.json中的buddleName修改为自己的,如果没有请到AGC上进行配置,参见 使用模拟器进行调试 。
四、项目结构


五、代码讲解
5.1 系统粘贴板基础功能介绍
系统粘贴板对象介绍
1.SystemPasteboard //系统粘贴板对象,亿华云计算定义系统粘贴板操作,包括复制、粘贴和设置粘贴板内容更改的侦听器。
2.PasteData//表示粘贴板上的粘贴数据。
3.PasteData.DataProperty //该类定义了系统粘贴板上 PasteData 的属性,包括时间戳、MIME 类型和其他属性数据。
4.PasteData.Record//该类将单个粘贴的数据定义为 Record,它可以是纯文本、HTML 文本、URI 和意图。 PasteData 对象包含一个或多个记录。
客户端(copy)CopyAbilitySlice.java
获取系统粘贴板,监听粘贴板数据变化
/** * 获取系统粘贴板 * 监听粘贴板数据变化 */ private void initPasteboard() { HiLog.debug(LABEL, "initPasteboard"); //获取系统粘贴板对象 pasteboard = SystemPasteboard.getSystemPasteboard(this); //监听粘贴板数据变化 pasteboard.addPasteDataChangedListener(() -> { if (pasteboard.hasPasteData()) { sync_text = getPasteData(); HiLog.debug(LABEL, "%{ public}s", "pasteStr:" + sync_text); } }); } 获取粘贴板内容
/** * 获取粘贴板记录 * * @return */ private String getPasteData() { HiLog.debug(LABEL, "getPasteData"); String result = ""; //粘贴板数据对象 PasteData pasteData = pasteboard.getPasteData(); if (pasteData == null) { return result; } PasteData.DataProperty dataProperty = pasteData.getProperty(); // boolean hasHtml = dataProperty.hasMimeType(PasteData.MIMETYPE_TEXT_HTML); boolean hasText = dataProperty.hasMimeType(PasteData.MIMETYPE_TEXT_PLAIN); //数据格式类型 if (hasHtml || hasText) { for (int i = 0; i < pasteData.getRecordCount(); i++) { //粘贴板数据记录 PasteData.Record record = pasteData.getRecordAt(i); //不同类型获取方式不同 String mimeType = record.getMimeType(); //HTML文本 if (mimeType.equals(PasteData.MIMETYPE_TEXT_HTML)) { result = record.getHtmlText(); //纯文本 } else if (mimeType.equals(PasteData.MIMETYPE_TEXT_PLAIN)) { result = record.getPlainText().toString(); // } else { HiLog.info(LABEL, "%{ public}s", "getPasteData mimeType :" + mimeType); } } } return result; } 设置文本到粘贴板中
/** * 设置文本到粘贴板 * * @param component */ private void setTextToPaste(Component component) { HiLog.info(LABEL, "setTextToPaste"); if (pasteboard != null) { String text = syncText.getText(); if (text.isEmpty()) { showTips(this, "请填写内容"); return; } //把记录添加到粘贴板 PasteData pasteData= PasteData.creatPlainTextData(text); //设置文本到粘贴板 pasteboard.setPasteData(pasteData); showTips(this, "复制成功"); HiLog.info(LABEL, "setTextToPaste succeeded"); } } 清空粘贴板
/** * 清空粘贴板 * * @param component */ private void clearPasteboard(Component component) { if (pasteboard != null) { pasteboard.clear(); showTips(this, "Clear succeeded"); } } 5.2 分布式粘贴板应用构建思路介绍

选择远端连接设备
本实例是通过新增加一个DevicesSelectAbility来实现的。
private void showDevicesDialog() { Intent intent = new Intent(); //打开选择设备的Ability页面DevicesSelectAbility Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName(getBundleName()) .withAbilityName(DevicesSelectAbility.class) .build(); intent.setOperation(operation); //携带一个设备选择请求标识,打开设备选择页面(DevicesSelectAbility) TODO startAbilityForResult(intent, Constants.PRESENT_SELECT_DEVICES_REQUEST_CODE); } /** * 打开设备选择Ability后,选择连接的设备执行setResult后触发 * * @param requestCode * @param resultCode * @param resultIntent */ @Override protected void onAbilityResult(int requestCode, int resultCode, Intent resultIntent) { HiLog.debug(LABEL, "onAbilityResult"); if (requestCode == Constants.PRESENT_SELECT_DEVICES_REQUEST_CODE && resultIntent != null) { //获取用户选择的设备 String devicesId = resultIntent.getStringParam(Constants.PARAM_DEVICE_ID); //连接粘贴板服务端 connectService(devicesId); return; } } 连接粘贴板服务端ServiceAbility服务
idl文件放在ohos.samples.pasteboard.paste目录下,Gradl窗口,执行compileDebugIdl 后,系统生成代理对象。
interface ohos.samples.pasteboard.paste.ISharePasteAgent { /* * 设置系统粘贴板 */ void setSystemPaste([in] String param); } 连接服务端ServiceAbility,如果组网中没有其他设备就连接本地的服务端。
连接成功后,初始化idl的服务器租用SharePasteAgentProxy代理,用于下一步的同步数据。
//idl共享粘贴板代理 private SharePasteAgentProxy remoteAgentProxy; /** * 连接粘贴板服务中心 */ private void connectService(String deviceId) { HiLog.debug(LABEL, "%{ public}s", "connectService"); if (!isConnect) { boolean isConnectRemote = deviceId != null; //三元表达式,判断连接本地还是远端Service Intent intent = isConnectRemote ? getRemoteServiceIntent(REMOTE_BUNDLE, REMOTE_SERVICE, deviceId) : getLocalServiceIntent(REMOTE_BUNDLE, REMOTE_SERVICE); HiLog.debug(LABEL, "%{ public}s", "intent:" + intent); //连接 Service connectAbility(intent, new IAbilityConnection() { @Override public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) { //发个通知,Service 连接成功了 eventHandler.sendEvent(EVENT_ABILITY_CONNECT_DONE); //初始化代理 remoteAgentProxy = new SharePasteAgentProxy(iRemoteObject); HiLog.debug(LABEL, "%{ public}s", "remoteAgentProxy:" + remoteAgentProxy); } @Override public void onAbilityDisconnectDone(ElementName elementName, int resultCode) { //发个通知,Service 断开连接了,主动断开不会执行,关闭服务端会执行 eventHandler.sendEvent(EVENT_ABILITY_DISCONNECT_DONE); } }); } } /** * 获取远端粘贴板服务中心 * * @param bundleName * @param serviceName * @return */ private Intent getRemoteServiceIntent(String bundleName, String serviceName, String deviceId) { HiLog.debug(LABEL, "%{ public}s", "getRemoteServiceIntent"); Operation operation = new Intent.OperationBuilder() .withDeviceId(deviceId) .withBundleName(bundleName) .withAbilityName(serviceName) //重要 .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) .build(); Intent intent = new Intent(); intent.setOperation(operation); return intent; } /** * 获取本地粘贴板服务中心 * * @param bundleName * @param serviceName * @return */ private Intent getLocalServiceIntent(String bundleName, String serviceName) { HiLog.debug(LABEL, "%{ public}s", "getLocalServiceIntent"); Operation operation = new Intent.OperationBuilder().withDeviceId("") .withBundleName(bundleName) .withAbilityName(serviceName) .build(); Intent intent = new Intent(); intent.setOperation(operation); return intent; } 同步数据到服务端
/** * 同步粘贴板记录到粘贴板服务中心 * * @param component */ private void syncData(Component component) { HiLog.debug(LABEL, "sync_text:" + sync_text); if (!sync_text.isEmpty()) { if (isConnect && remoteAgentProxy != null) { //调用服务端IPC方法 try { remoteAgentProxy.setSystemPaste(sync_text); //更换文本 syncText.setText(getRandomText()); sync_text = ""; showTips(this, "同步成功"); } catch (RemoteException remoteException) { remoteException.printStackTrace(); } } else { showTips(this, "正在连接设备"); } } else { showTips(this, "点击复制到粘贴板"); } } 随机生成粘贴文本
/** * 随机文本,模拟数据 * * @return */ public String getRandomText() { List<String> list = Arrays.asList( "快马加鞭未下鞍,离天三尺三", "我自横刀向天笑,去留肝胆两昆仑", "飞流直下三千尺,疑是银河落九天", "君子求诸己,小人求诸人", "吾日三省吾身:为人谋而不忠乎?与朋友交而不信乎?传不习乎?"); int random = new SecureRandom().nextInt(list.size()); return list.get(random); } 服务端(paste)ServiceAbility.java
设置粘贴板服务
idl文件放在ohos.samples.pasteboard.paste目录下,Gradl窗口,执行compileDebugIdl 后,系统生成代理对象,idl提供了setSystemPaste接口供远端调用。
interface ohos.samples.pasteboard.paste.ISharePasteAgent { /* * 设置系统粘贴板 */ void setSystemPaste([in] String param); } //idl的服务端实现, SharePasteAgentStub sharePasteAgentStub = new SharePasteAgentStub(DESCRIPTOR) { @Override public void setSystemPaste(String param) { HiLog.info(LABEL, "%{ public}s", "param:" + param); //插入数据库 ItemChild itemChild = new ItemChild(); String currentTime = DateUtils.getCurrentDate("yyMMdd HH:mm:ss"); itemChild.setWriteTime(currentTime); itemChild.setWriteContent(param); itemChild.setIndex(String.valueOf(UUID.randomUUID())); //默认添加到未分类 itemChild.setTagName(Const.CATEGORY_TAG_UNCATEGOORIZED); //添加粘贴板记录到分布式数据库 kvManagerUtils.addItemChild(itemChild); } }; @Override protected IRemoteObject onConnect(Intent intent) { HiLog.info(LABEL, "%{ public}s", "ServiceAbility onConnect"); return sharePasteAgentStub; } **初始化数据库** ```java //初始化数据库工具 kvManagerUtils = KvManagerUtils.getInstance(this); //初始化数据库管理对象 kvManagerUtils.initDbManager(eventHandler); //初始化数据库数据按钮 Image initDb = (Image) findComponentById(ResourceTable.Id_init_db); initDb.setClickedListener(component -> { //默认选中“未定义”标签 current_select_category_index = 0; //初始化数据库数据 kvManagerUtils.initDbData(); showTip("初始化完成"); }); 初始化数据列表
/** * 从分布式数据库中查询数据 */ public void queryData() { HiLog.debug(LABEL, "queryData"); try { //加载选中类别下的数据列表 initItemChild(kvManagerUtils.queryDataByTag(CategoryData.tagList.get(current_select_category_index))); } catch (KvStoreException exception) { HiLog.info(LABEL, "the value must be String"); } } /** * 初始化选中标签的子项列表 * * @param itemChildList itemChildList, the bean of itemChild */ private void initItemChild(List<ItemChild> itemChildList) { HiLog.debug(LABEL, "initItemChild:" + itemChildList); if (itemChildList == null) { return; } //清空组件 itemChildLayout.removeAllComponents(); // Create itemChild for (ItemChild itemChild : itemChildList) { //获取子项类别所在的组件 Component childComponent = LayoutScatter.getInstance(this).parse(ResourceTable.Layout_paste_record_per, null, false); //写入时间 Text writeTime = (Text) childComponent.findComponentById(ResourceTable.Id_writeTime); writeTime.setText(itemChild.getWriteTime()); //粘贴板内容 Text writeContent = (Text) childComponent.findComponentById(ResourceTable.Id_writeContent); writeContent.setText(itemChild.getWriteContent()); //复制按钮 Text copy = (Text) childComponent.findComponentById(ResourceTable.Id_itemChildPerCopy); //复制按钮的监听事件 copy.setClickedListener(component -> { //复制内容到粘贴板 pasteboard.setPasteData(PasteData.creatPlainTextData(itemChild.getWriteContent())); showTip("已复制到粘贴板"); }); //收藏按钮 Text favorite = (Text) childComponent.findComponentById(ResourceTable.Id_itemChildPerFavorite); //收藏按钮的监听事件 favorite.setClickedListener(component -> { //修改标签微已收藏 itemChild.setTagName(Const.CATEGORY_TAG_FAVORITED); //保存数据 kvManagerUtils.addItemChild(itemChild); showTip("已加入到收藏中"); }); /