栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 前沿技术 > 云计算 > Docker/k8s

HarmonyOS Sample 之 Pasteboard 分布式粘贴板

Docker/k8s 更新时间:发布时间: 百科书网 趣学号



想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

一、介绍

HarmonyOS提供系统剪贴板服务的操作接口,支持用户程序从系统剪贴板中读取、写入和查询剪贴板数据,以及添加、移除系统剪贴板数据变化的回调。

设备内:

用户通过系统剪贴板服务,可实现应用之间的简单数据传递。例如:在应用A中复制的数据,可以在应用B中粘贴,反之亦可。

设备间:

在分布式粘贴板场景中,粘贴的数据可以跨设备写入。例如,设备A上的应用程序使用系统粘贴板接口将从设备A复制的数据通过IDL接口存储到设备B的系统粘贴板中。如果数据允许,设备B上的应用程序可以读取并粘贴系统粘贴板中的复制数据。实现设备之间粘贴板的分布式协同。

基于以上理解,实现一个分布式粘贴板应用程序,应用程序分为客户端(copy)和服务端(paste)两部分,通过idl实现数据传递。

客户端负责数据采集,服务端负责数据的展示和应用,客户端和服务端可以安装在同一台设备中,也可以安装在不同的设备中,服务端也可以按照在多台设备中,服务端通过分布式数据库实现粘贴板数据的自动同步。

二、效果展示

HarmonyOS Sample 之 Pasteboard 分布式粘贴板-鸿蒙HarmonyOS技术社区

HarmonyOS Sample 之 Pasteboard 分布式粘贴板-鸿蒙HarmonyOS技术社区

三、搭建环境

安装DevEco Studio,详情请参考DevEco Studio下载。

设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:

如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。

如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。

下载源码后,使用DevEco Studio 打开项目,模拟器运行即可。

真机运行需要将config.json中的buddleName修改为自己的,如果没有请到AGC上进行配置,参见 使用模拟器进行调试 。

四、项目结构

HarmonyOS Sample 之 Pasteboard 分布式粘贴板-鸿蒙HarmonyOS技术社区


HarmonyOS Sample 之 Pasteboard 分布式粘贴板-鸿蒙HarmonyOS技术社区

五、代码讲解

5.1 系统粘贴板基础功能介绍

系统粘贴板对象介绍

1.SystemPasteboard //系统粘贴板对象,定义系统粘贴板操作,包括复制、粘贴和设置粘贴板内容更改的侦听器。

2.PasteData//表示粘贴板上的粘贴数据。

3.PasteData.DataProperty //该类定义了系统粘贴板上 PasteData 的属性,包括时间戳、MIME 类型和其他属性数据。

4.PasteData.Record//该类将单个粘贴的数据定义为 Record,它可以是纯文本、HTML 文本、URI 和意图。 PasteData 对象包含一个或多个记录。

客户端(copy)CopyAbilitySlice.java

获取系统粘贴板,监听粘贴板数据变化

  1.  
  2. private void initPasteboard() {     HiLog.debug(LABEL, "initPasteboard"); 
  3.     //获取系统粘贴板对象     pasteboard = SystemPasteboard.getSystemPasteboard(this); 
  4.     //监听粘贴板数据变化     pasteboard.addPasteDataChangedListener(() -> { 
  5.         if (pasteboard.hasPasteData()) {             sync_text = getPasteData(); 
  6.             HiLog.debug(LABEL, "%{public}s", "pasteStr:" + sync_text);         } 
  7.     }); } 

获取粘贴板内容

  1.  private String getPasteData() { 
  2.     HiLog.debug(LABEL, "getPasteData");     String result = ""; 
  3.      //粘贴板数据对象 
  4.     PasteData pasteData = pasteboard.getPasteData();     if (pasteData == null) { 
  5.         return result;     } 
  6.     PasteData.DataProperty dataProperty = pasteData.getProperty();     // 
  7.     boolean hasHtml = dataProperty.hasMimeType(PasteData.MIMETYPE_TEXT_HTML);     boolean hasText = dataProperty.hasMimeType(PasteData.MIMETYPE_TEXT_PLAIN); 
  8.      //数据格式类型 
  9.     if (hasHtml || hasText) {         for (int i = 0; i < pasteData.getRecordCount(); i++) { 
  10.             //粘贴板数据记录             PasteData.Record record = pasteData.getRecordAt(i); 
  11.             //不同类型获取方式不同             String mimeType = record.getMimeType(); 
  12.             //HTML文本             if (mimeType.equals(PasteData.MIMETYPE_TEXT_HTML)) { 
  13.                 result = record.getHtmlText();                 //纯文本 
  14.             } else if (mimeType.equals(PasteData.MIMETYPE_TEXT_PLAIN)) {                 result = record.getPlainText().toString(); 
  15.                 //             } else { 
  16.                 HiLog.info(LABEL, "%{public}s", "getPasteData mimeType :" + mimeType);             } 
  17.         }     } 
  18.     return result; } 

设置文本到粘贴板中

  1.  private void setTextToPaste(Component component) { 
  2.     HiLog.info(LABEL, "setTextToPaste");     if (pasteboard != null) { 
  3.         String text = syncText.getText();         if (text.isEmpty()) { 
  4.             showTips(this, "请填写内容");             return; 
  5.         }         //把记录添加到粘贴板 
  6.         PasteData pasteData=  PasteData.creatPlainTextData(text);         //设置文本到粘贴板 
  7.         pasteboard.setPasteData(pasteData);  
  8.         showTips(this, "复制成功");         HiLog.info(LABEL, "setTextToPaste succeeded"); 
  9.     } } 

清空粘贴板

  1.  private void clearPasteboard(Component component) { 
  2.     if (pasteboard != null) {         pasteboard.clear(); 
  3.         showTips(this, "Clear succeeded");     } 

5.2 分布式粘贴板应用构建思路介绍


HarmonyOS Sample 之 Pasteboard 分布式粘贴板-鸿蒙HarmonyOS技术社区

选择远端连接设备

本实例是通过新增加一个DevicesSelectAbility来实现的。

  1. private void showDevicesDialog() {     Intent intent = new Intent(); 
  2.     //打开选择设备的Ability页面DevicesSelectAbility     Operation operation = 
  3.             new Intent.OperationBuilder()                     .withDeviceId("") 
  4.                     .withBundleName(getBundleName())                     .withAbilityName(DevicesSelectAbility.class) 
  5.                     .build();     intent.setOperation(operation); 
  6.     //携带一个设备选择请求标识,打开设备选择页面(DevicesSelectAbility) TODO     startAbilityForResult(intent, Constants.PRESENT_SELECT_DEVICES_REQUEST_CODE); 
  7. }  
  8.  @Override 
  9. protected void onAbilityResult(int requestCode, int resultCode, Intent resultIntent) {     HiLog.debug(LABEL, "onAbilityResult"); 
  10.     if (requestCode == Constants.PRESENT_SELECT_DEVICES_REQUEST_CODE && resultIntent != null) {         //获取用户选择的设备 
  11.         String devicesId = resultIntent.getStringParam(Constants.PARAM_DEVICE_ID);         //连接粘贴板服务端 
  12.         connectService(devicesId);         return; 
  13.     } } 

连接粘贴板服务端ServiceAbility服务

idl文件放在ohos.samples.pasteboard.paste目录下,Gradl窗口,执行compileDebugIdl 后,系统生成代理对象。

  1. interface ohos.samples.pasteboard.paste.ISharePasteAgent {      
  2.     void setSystemPaste([in] String param); } 

连接服务端ServiceAbility,如果组网中没有其他设备就连接本地的服务端。

连接成功后,初始化idl的SharePasteAgentProxy代理,用于下一步的同步数据。

  1. //idl共享粘贴板代理 private SharePasteAgentProxy remoteAgentProxy; 
  2.   
  3. private void connectService(String deviceId) {     HiLog.debug(LABEL, "%{public}s", "connectService"); 
  4.     if (!isConnect) {         boolean isConnectRemote = deviceId != null; 
  5.         //三元表达式,判断连接本地还是远端Service         Intent intent = isConnectRemote 
  6.                 ? getRemoteServiceIntent(REMOTE_BUNDLE, REMOTE_SERVICE, deviceId)                 : getLocalServiceIntent(REMOTE_BUNDLE, REMOTE_SERVICE); 
  7.          HiLog.debug(LABEL, "%{public}s", "intent:" + intent); 
  8.         //连接 Service         connectAbility(intent, new IAbilityConnection() { 
  9.             @Override             public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) { 
  10.                 //发个通知,Service 连接成功了                 eventHandler.sendEvent(EVENT_ABILITY_CONNECT_DONE); 
  11.                 //初始化代理                 remoteAgentProxy = new SharePasteAgentProxy(iRemoteObject); 
  12.                 HiLog.debug(LABEL, "%{public}s", "remoteAgentProxy:" + remoteAgentProxy);             } 
  13.              @Override 
  14.             public void onAbilityDisconnectDone(ElementName elementName, int resultCode) {                 //发个通知,Service 断开连接了,主动断开不会执行,关闭服务端会执行 
  15.                 eventHandler.sendEvent(EVENT_ABILITY_DISCONNECT_DONE);             } 
  16.         });     } 
  17. }  
  18. private Intent getRemoteServiceIntent(String bundleName, String serviceName, String deviceId) {     HiLog.debug(LABEL, "%{public}s", "getRemoteServiceIntent"); 
  19.     Operation operation = new Intent.OperationBuilder()             .withDeviceId(deviceId) 
  20.             .withBundleName(bundleName)             .withAbilityName(serviceName) 
  21.             //重要             .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) 
  22.             .build();     Intent intent = new Intent(); 
  23.     intent.setOperation(operation);     return intent; 
  24. }  
  25.  private Intent getLocalServiceIntent(String bundleName, String serviceName) { 
  26.     HiLog.debug(LABEL, "%{public}s", "getLocalServiceIntent");     Operation operation = new Intent.OperationBuilder().withDeviceId("") 
  27.             .withBundleName(bundleName)             .withAbilityName(serviceName) 
  28.             .build();     Intent intent = new Intent(); 
  29.     intent.setOperation(operation);     return intent; 

同步数据到服务端

  1.  private void syncData(Component component) { 
  2.     HiLog.debug(LABEL, "sync_text:" + sync_text);     if (!sync_text.isEmpty()) { 
  3.         if (isConnect && remoteAgentProxy != null) {             //调用服务端IPC方法 
  4.             try {                 remoteAgentProxy.setSystemPaste(sync_text); 
  5.                 //更换文本                 syncText.setText(getRandomText()); 
  6.                 sync_text = "";                 showTips(this, "同步成功"); 
  7.             } catch (RemoteException remoteException) {                 remoteException.printStackTrace(); 
  8.             }         } else { 
  9.             showTips(this, "正在连接设备");         } 
  10.     } else {         showTips(this, "点击复制到粘贴板"); 
  11.     } } 

随机生成粘贴文本

  1.  public String getRandomText() { 
  2.     List list = Arrays.asList(             "快马加鞭未下鞍,离天三尺三", 
  3.             "我自横刀向天笑,去留肝胆两昆仑",             "飞流直下三千尺,疑是银河落九天", 
  4.             "君子求诸己,小人求诸人",             "吾日三省吾身:为人谋而不忠乎?与朋友交而不信乎?传不习乎?"); 
  5.     int random = new SecureRandom().nextInt(list.size());     return list.get(random); 

服务端(paste)ServiceAbility.java

设置粘贴板服务

idl文件放在ohos.samples.pasteboard.paste目录下,Gradl窗口,执行compileDebugIdl 后,系统生成代理对象,idl提供了setSystemPaste接口供远端调用。

  1. interface ohos.samples.pasteboard.paste.ISharePasteAgent {      
  2.     void setSystemPaste([in] String param); } 
  1. //idl的服务端实现, SharePasteAgentStub sharePasteAgentStub = new SharePasteAgentStub(DEscriptOR) { 
  2.     @Override     public void setSystemPaste(String param) { 
  3.         HiLog.info(LABEL, "%{public}s", "param:" + param);  
  4.         //插入数据库         ItemChild itemChild = new ItemChild(); 
  5.         String currentTime = DateUtils.getCurrentDate("yyMMdd HH:mm:ss");         itemChild.setWriteTime(currentTime); 
  6.         itemChild.setWriteContent(param);         itemChild.setIndex(String.valueOf(UUID.randomUUID())); 
  7.         //默认添加到未分类         itemChild.setTagName(Const.CATEGORY_TAG_UNCATEGOORIZED); 
  8.         //添加粘贴板记录到分布式数据库         kvManagerUtils.addItemChild(itemChild); 
  9.      } 
  10. };  
  11. @Override protected IRemoteObject onConnect(Intent intent) { 
  12.     HiLog.info(LABEL, "%{public}s", "ServiceAbility onConnect");     return sharePasteAgentStub; 
  13. }  
  14. **初始化数据库** ```java 
  15. //初始化数据库工具 kvManagerUtils = KvManagerUtils.getInstance(this); 
  16.  //初始化数据库管理对象 
  17. kvManagerUtils.initDbManager(eventHandler);  
  18. //初始化数据库数据按钮 Image initDb = (Image) findComponentById(ResourceTable.Id_init_db); 
  19. initDb.setClickedListener(component -> {     //默认选中“未定义”标签 
  20.     current_select_category_index = 0;     //初始化数据库数据 
  21.     kvManagerUtils.initDbData();     showTip("初始化完成"); 
  22. }); 

初始化数据列表

  1.  public void queryData() { 
  2.     HiLog.debug(LABEL, "queryData");     try { 
  3.         //加载选中类别下的数据列表         initItemChild(kvManagerUtils.queryDataByTag(CategoryData.tagList.get(current_select_category_index))); 
  4.     } catch (KvStoreException exception) {         HiLog.info(LABEL, "the value must be String"); 
  5.     } } 
  6.   
  7. private void initItemChild(List itemChildList) {     HiLog.debug(LABEL, "initItemChild:" + itemChildList); 
  8.     if (itemChildList == null) {         return; 
  9.     }     //清空组件 
  10.     itemChildLayout.removeAllComponents();     // Create itemChild 
  11.     for (ItemChild itemChild : itemChildList) {         //获取子项类别所在的组件 
  12.         Component childComponent =                 LayoutScatter.getInstance(this).parse(ResourceTable.Layout_paste_record_per, null, false); 
  13.         //写入时间         Text writeTime = (Text) childComponent.findComponentById(ResourceTable.Id_writeTime); 
  14.         writeTime.setText(itemChild.getWriteTime());         //粘贴板内容 
  15.         Text writeContent = (Text) childComponent.findComponentById(ResourceTable.Id_writeContent);         writeContent.setText(itemChild.getWriteContent()); 
  16.          //复制按钮 
  17.         Text copy = (Text) childComponent.findComponentById(ResourceTable.Id_itemChildPerCopy);         //复制按钮的监听事件 
  18.         copy.setClickedListener(component -> {             //复制内容到粘贴板 
  19.             pasteboard.setPasteData(PasteData.creatPlainTextData(itemChild.getWriteContent()));             showTip("已复制到粘贴板"); 
  20.         });  
  21.         //收藏按钮         Text favorite = (Text) childComponent.findComponentById(ResourceTable.Id_itemChildPerFavorite); 
  22.         //收藏按钮的监听事件         favorite.setClickedListener(component -> { 
  23.             //修改标签微已收藏             itemChild.setTagName(Const.CATEGORY_TAG_FAVORITED); 
  24.             //保存数据             kvManagerUtils.addItemChild(itemChild); 
  25.             showTip("已加入到收藏中");         }); 
  26.   
  27.                  //复选框 
  28.         Checkbox noteId = (Checkbox) childComponent.findComponentById(ResourceTable.Id_noteId);         //子项列表的点击事件 
  29.         childComponent.setClickedListener(component -> {             if (noteId.getVisibility() == Component.VISIBLE) { 
  30.                 noteId.setChecked(!noteId.isChecked());             } 
  31.         });         //子项列表的长按事件,长按显示复选框 
  32.         childComponent.setLongClickedListener(component -> {             //checkbox显示 
  33.             noteId.setVisibility(Component.VISIBLE);             //设置复选框样式,以及其他文本组件的缩进 
  34.             Element element = ElementScatter.getInstance(getContext()).parse(ResourceTable.Graphic_check_box_checked);             noteId.setBackground(element); 
  35.             noteId.setChecked(true);             writeTime.setMarginLeft(80); 
  36.             writeContent.setMarginLeft(80);         }); 
  37.         //复选框的状态变化监听事件,state表示是否被选中         noteId.setCheckedStateChangedListener((component, state) -> { 
  38.             // 状态改变的逻辑             Element element; 
  39.             if (state) {                 //设置选中的样式 
  40.                 element = ElementScatter.getInstance(getContext())                         .parse(ResourceTable.Graphic_check_box_checked); 
  41.             } else {                 //设置未选中的样式 
  42.                 element = ElementScatter.getInstance(getContext())                         .parse(ResourceTable.Graphic_check_box_uncheck); 
  43.             }             noteId.setBackground(element); 
  44.         });          
  45.          //添加子项列表组件到布局 
  46.         itemChildLayout.addComponent(childComponent);     } 

标签分类显示

  1. //初始化列表列表的点击的监听事件 categoryList.setItemClickedListener( 
  2.         (listContainer, component, index, l1) -> {             //点的就是当前类别 
  3.             if (categoryListProvider.getSelectIndex() == index) {                 return; 
  4.             }             //切换类别索引 
  5.             categoryListProvider.setSelectIndex(index);             //设置选中的标签索引 
  6.             current_select_category_index = index;             //获取当前选中的标签名称 
  7.             String tagName = CategoryData.tagList.get(index);             //从数据库中查询标签子项列表 
  8.             initItemChild(kvManagerUtils.queryDataByTagAndKewWord(searchTextField.getText(), tagName));             //通知数据更新 
  9.             categoryListProvider.notifyDataChanged();             //滚动条到最顶部 
  10.             itemListScroll.fluentScrollYTo(0);         }); 

搜索粘贴记录

  1. //搜索key监听事件 searchTextField.setKeyEventListener( 
  2.         (component, keyEvent) -> {             if (keyEvent.isKeyDown() && keyEvent.getKeyCode() == KeyEvent.KEY_ENTER) { 
  3.                 //获取当前选中的标签名称                 String tagName = CategoryData.tagList.get(current_select_category_index); 
  4.                 List itemChildList = kvManagerUtils.queryDataByTagAndKewWord(searchTextField.getText(), tagName);                 //从数据库中查询标签子项列表 
  5.                 initItemChild(itemChildList);                 //通知数据更新 
  6.                 categoryListProvider.notifyDataChanged();                 //滚动条到最顶部 
  7.                 itemListScroll.fluentScrollYTo(0);             } 
  8.             return false;         }); 

分布式数据库工具KvManagerUtils.java

数据变化通知

提供了分布式数据库管理工具KvManagerUtils.java,数据库操作都集中在这里了。

为了在数据库数据发生变化时能及时更新页面显示,页面在初始化数据库时,传递eventHandler对象,这样在数据库变化是可以通知到页面。

  1.  
  2. private void subscribeDb(SingleKvStore singleKvStore) {     HiLog.info(LABEL, "subscribeDb"); 
  3.     //数据库观察者客户端     KvStoreObserver kvStoreObserverClient = new KvStoreObserverClient(); 
  4.     //订阅远程数据更改     singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_REMOTE, kvStoreObserverClient); 
  5. }  
  6.  private class KvStoreObserverClient implements KvStoreObserver { 
  7.     @Override     public void onChange(ChangeNotification notification) { 
  8.         HiLog.error(LABEL, "onChange");         eventHandler.sendEvent(Const.DB_CHANGE_MESS); 
  9.     } } 

数据自动同步

默认开启自动同步

  1.  
  2. public void initDbManager(EventHandler eventHandler) {     this.eventHandler = eventHandler; 
  3.     HiLog.info(LABEL, "initDbManager");     if (singleKvStore == null || kvManager == null) { 
  4.         HiLog.info(LABEL, "initDbData");         //创建数据库管理员 
  5.         kvManager = createManager();         //创建数据库 
  6.         singleKvStore = createDb(kvManager);         subscribeDb(singleKvStore); 
  7.      } 
  8. }  
  9.   
  10.  
  11. private KvManager createManager() {     HiLog.info(LABEL, "createManager"); 
  12.     KvManager manager = null;     try { 
  13.         //         KvManagerConfig config = new KvManagerConfig(context); 
  14.         manager = KvManagerFactory.getInstance().createKvManager(config);     } catch (KvStoreException exception) { 
  15.         HiLog.error(LABEL, "some exception happen");     } 
  16.     return manager; } 
  17.   
  18. private SingleKvStore createDb(KvManager kvManager) {     HiLog.info(LABEL, "createDb"); 
  19.     SingleKvStore kvStore = null;     try { 
  20.         Options options = new Options();         //单版本数据库,不加密,没有可用的 KvStore 数据库就创建 
  21.         //单版本分布式数据库,默认开启组网设备间自动同步功能,         //如果应用对性能比较敏感建议设置关闭自动同步功能setAutoSync(false),主动调用sync接口同步。 
  22.         options.setCreateIfMissing(true)                 .setEncrypt(false) 
  23.                 .setKvStoreType(KvStoreType.SINGLE_VERSION);         //创建数据库 
  24.         kvStore = kvManager.getKvStore(options, STORE_ID);  
  25.     } catch (KvStoreException exception) {         HiLog.error(LABEL, "some exception happen"); 
  26.     }     return kvStore; 

权限config.json

  1. "reqPermissions": [     { 
  2.       "name": "ohos.permission.DISTRIBUTED_DATASYNC",       "reason": "同步粘贴板数据", 
  3.       "usedScene": {         "when": "inuse", 
  4.         "ability": [           "ohos.samples.pasteboard.paste.MainAbility", 
  5.           "ohos.samples.pasteboard.paste.ServiceAbility"         ] 
  6.       }     }, 
  7.     {       "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" 
  8.     },     { 
  9.       "name": "ohos.permission.KEEP_BACKGROUND_RUNNING"     } 
  10.   ] 
六、思考总结

1.粘贴板板传递数据可能会存在安全问题,需要注意,要根据具体场景来使用。

设备内每次传输的粘贴数据大小不能超过 800 KB。每次设备间传输的数据不能超过64KB,且数据必须为文本格式。

2.idl的使用,在上述案例中,客户端(copy) 和 服务端(paste) 项目idl下内容完全一致即可。


HarmonyOS Sample 之 Pasteboard 分布式粘贴板-鸿蒙HarmonyOS技术社区

HarmonyOS Sample 之 Pasteboard 分布式粘贴板-鸿蒙HarmonyOS技术社区

七、完整代码

附件可以直接下载

https://harmonyos.51cto.com/resource/1489

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com



 

转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/796691.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号