精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

HarmonyOS 分布式親子教育

系統(tǒng) 分布式 OpenHarmony
本篇Codelab通過一個(gè)親子早教系統(tǒng),完整的為您介紹了早教算數(shù)題和益智拼圖游戲兩個(gè)綜合案例,旨在幫助您快速了解HarmonyOS應(yīng)用開發(fā)、多屏互動(dòng)、分布式跨設(shè)備協(xié)同的功能了解。

[[412544]]

想了解更多內(nèi)容,請(qǐng)?jiān)L問:

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos.51cto.com

1. 項(xiàng)目介紹

遠(yuǎn)程教育,多屏協(xié)同是智慧教育的一個(gè)重要場(chǎng)景。本篇Codelab通過一個(gè)親子早教系統(tǒng),完成了分布式早教算數(shù)題和分布式拼圖游戲兩個(gè)綜合案例,旨在幫助開發(fā)者快速了解HarmonyOS應(yīng)用開發(fā)、多屏互動(dòng)和分布式跨設(shè)備協(xié)同的體驗(yàn)。

本篇Codelab將為您重點(diǎn)介紹Page Ability、Service Ability、Intent以及分布式任務(wù)調(diào)度、公共事件等。同時(shí)我們還將為您介紹多屏互動(dòng),分布式跨設(shè)備協(xié)同、繪圖畫布的使用、經(jīng)典拼圖算法。正式介紹之前,我們先對(duì)親子早教系統(tǒng)進(jìn)行展示,讓您快速了解到本篇Codelab所實(shí)現(xiàn)的功能。

功能1:早教算數(shù)題

點(diǎn)擊早教算數(shù)題,系統(tǒng)會(huì)為您隨機(jī)出一道兩位數(shù)的加法,點(diǎn)擊實(shí)時(shí)輔導(dǎo)會(huì)拉起兩個(gè)畫布,本地端可以用黑色筆跡進(jìn)行草稿運(yùn)算,遠(yuǎn)程端可以用紅色筆跡進(jìn)行實(shí)時(shí)指導(dǎo),操作步驟兩端實(shí)時(shí)同步,效果圖如下所示。

HarmonyOS 分布式親子教育-鴻蒙HarmonyOS技術(shù)社區(qū)

功能2:益智拼圖游戲

點(diǎn)擊益智拼圖游戲會(huì)拉起一個(gè)九宮格的拼圖游戲(圖片會(huì)隨機(jī)亂序),點(diǎn)擊圖片可以進(jìn)行拼圖。在本地端點(diǎn)擊親子協(xié)同,遠(yuǎn)程端會(huì)拉起一個(gè)一模一樣的游戲頁(yè)面,兩個(gè)設(shè)備可以進(jìn)行實(shí)時(shí)互動(dòng),同步對(duì)圖片進(jìn)行拼接,操作步驟亦可實(shí)時(shí)同步,效果圖如下所示。

HarmonyOS 分布式親子教育-鴻蒙HarmonyOS技術(shù)社區(qū)

想知道上述兩個(gè)親子游戲是如何實(shí)現(xiàn)的嗎?快來跟隨我們的Codelab進(jìn)行學(xué)習(xí)吧。

2. 搭建HarmonyOS環(huán)境

搭建HarmonyOS環(huán)境

  • 安裝DevEco Studio,詳情請(qǐng)參考DevEco Studio下載。
  • 設(shè)置DevEco Studio開發(fā)環(huán)境,DevEco Studio開發(fā)環(huán)境需要依賴于網(wǎng)絡(luò)環(huán)境,需要連接上網(wǎng)絡(luò)才能確保工具的正常使用,可以根據(jù)如下兩種情況來配置開發(fā)環(huán)境
  1. 如果可以直接訪問Internet,只需進(jìn)行下載HarmonyOS SDK操作
  2. 如果網(wǎng)絡(luò)不能直接訪問Internet,需要通過代理服務(wù)器才可以訪問,請(qǐng)參考配置開發(fā)環(huán)境

說明:

如需要在手機(jī)中運(yùn)行程序,則需要提前申請(qǐng)證書,如使用模擬器可忽略

準(zhǔn)備密鑰和證書請(qǐng)求文件

申請(qǐng)調(diào)試證書

你可以通過如下設(shè)備完成本CodeLab:

開啟開發(fā)者模式的HarmonyOS真機(jī)

DevEco Studio中的手機(jī)模擬器(模擬器暫不支持分布式調(diào)試)

3. 代碼結(jié)構(gòu)解讀

本篇Codelab我們只是對(duì)核心代碼進(jìn)行講解,您可以在最后的參考中下載完整代碼,首先來介紹下整個(gè)工程的代碼結(jié)構(gòu):

HarmonyOS 分布式親子教育-鴻蒙HarmonyOS技術(shù)社區(qū)
  • devices:封裝了選擇設(shè)備的Dialog,您可直接調(diào)用SelectDeviceDialog里面的相關(guān)方法。
  • point:封裝了繪圖的相關(guān)功能,您可直接調(diào)用DrawPoint里面的相關(guān)方法。
  • slice:MainAbilitySlice為應(yīng)用主頁(yè)面,MathGameAbilitySlice為早教算數(shù)題主頁(yè)面,MathDrawRemSlice為早教算數(shù)題繪圖頁(yè)面,PictureGameAbilitySlice為拼圖游戲主頁(yè)面。
  • utils:封裝了公共方法和公共數(shù)據(jù)。
  • MathGameServiceAbility、PictureGameServiceAbility:供遠(yuǎn)端連接的Service Ability。
  • resources:存放工程使用到的資源文件,其中resources\base\layout下存放xml布局文件;resources\base\media下存放圖片資源。
  • config.json:配置文件。

4. 親子早教系統(tǒng)頁(yè)面流轉(zhuǎn)

親子早教系統(tǒng)主頁(yè)面和各個(gè)游戲頁(yè)面的布局如下圖所示,首先給大家介紹一下相關(guān)的布局代碼。

HarmonyOS 分布式親子教育-鴻蒙HarmonyOS技術(shù)社區(qū)

首頁(yè)的布局文件為ability_main.xml、頁(yè)面控制邏輯MainAbilitySlice;早教系統(tǒng)的布局文件為math_game.xml、頁(yè)面控制邏輯MathGameAbilitySlice;拼圖游戲的布局文件為ability_picture.xml、頁(yè)面控制邏輯PictureGameAbilitySlice。要實(shí)現(xiàn)頁(yè)面的相互流轉(zhuǎn)首先需要在MainAbility中設(shè)置路由,并在MainAbilitySlice中實(shí)現(xiàn)按鈕的點(diǎn)擊事件。

以跳轉(zhuǎn)早教算數(shù)題為例,首先需要在MainAbility中設(shè)置路由,代碼如下所示:

  1. addActionRoute(CommonData.MATH_PAGE, MathGameAbilitySlice.class.getName()); 

 而后,需要在MainAbilitySlice中添加點(diǎn)擊事件,代碼如下所示:

  1. private void mathGame() {  
  2.     LogUtil.info(TAG, "Click ResourceTable Id_math_game");  
  3.     Intent mathGameIntent = new Intent();  
  4.     Operation operationMath = new Intent.OperationBuilder()  
  5.             .withBundleName(getBundleName())  
  6.             .withAbilityName(CommonData.ABILITY_MAIN)  
  7.             .withAction(CommonData.MATH_PAGE)  
  8.             .build();  
  9.     mathGameIntent.setOperation(operationMath);  
  10.     startAbility(mathGameIntent);  

 至此,您已實(shí)現(xiàn)跳轉(zhuǎn)到早教算數(shù)題和益智拼圖游戲兩個(gè)頁(yè)面了:

  • 早教算數(shù)題的出題邏輯可以參考setQuestion和checkAnswer兩個(gè)方法
  • 拼圖游戲圖片亂序、移動(dòng)圖片、判斷游戲結(jié)束、重新開始可分別參考pictureRandom、moveFun、gameOverFun、restartFun相關(guān)方法

因篇幅有限且相關(guān)游戲算法不屬于本篇Codelab想為您重點(diǎn)介紹的HarmonyOS特性,因此不再贅述,您可自行調(diào)用相關(guān)函數(shù)、理解相關(guān)代碼實(shí)現(xiàn)游戲功能,附件代碼中也已進(jìn)行了詳細(xì)的注釋。以上即完成了單機(jī)版本的HarmonyOS應(yīng)用開發(fā),接下來我們將為您重點(diǎn)介紹基于HarmonyOS分布式能力的相關(guān)系統(tǒng)設(shè)計(jì)。

5. 發(fā)現(xiàn)設(shè)備和建立連接

在早教算數(shù)題中點(diǎn)擊實(shí)時(shí)輔導(dǎo)或者在拼圖游戲中點(diǎn)擊親子協(xié)同,會(huì)彈出選擇設(shè)備頁(yè)面,如下圖所示。我們已經(jīng)在devices目錄下為您封裝好了選擇設(shè)備的dialog,具體代碼請(qǐng)參考DevicesListAdapter(設(shè)備列表適配器)、item_device_list.xml (設(shè)備列表item布局)、SelectDeviceDialog(選擇設(shè)備列表的彈窗)、dialog_select_device.xml(彈窗布局)。

HarmonyOS 分布式親子教育-鴻蒙HarmonyOS技術(shù)社區(qū)

SelectDeviceDialog中選擇設(shè)備彈窗的構(gòu)造函數(shù)如下,需要傳入三個(gè)參數(shù):上下文、設(shè)備列表、選擇結(jié)果的回調(diào)事件。在業(yè)務(wù)代碼中調(diào)用如下構(gòu)造函數(shù)即可打開選擇設(shè)備的彈窗,代碼如下所示:

  1. public SelectDeviceDialog(Context context, List<DeviceInfo> devices, SelectResultListener listener) {  
  2.     initView(context, devices, listener);  

 其中List devices可以通過如下函數(shù)去獲取,代碼如下所示:

  1. private void getDevices() {  
  2.     if (devices.size() > 0) {  
  3.         devices.clear();  
  4.     }  
  5.     List<DeviceInfo> deviceInfos =  
  6.             DeviceManager.getDeviceList(ohos.distributedschedule.interwork.DeviceInfo.FLAG_GET_ONLINE_DEVICE);  
  7.     LogUtil.info(TAG, "deviceInfos size is :" + deviceInfos.size());  
  8.     devices.addAll(deviceInfos);  
  9.     showDevicesDialog();  

 選擇結(jié)果的回調(diào)事件可以根據(jù)業(yè)務(wù)的不同傳入不同的回調(diào)事件,例如早教算數(shù)題中選擇設(shè)備后,需要拉起本地端和遠(yuǎn)程端兩個(gè)畫布,此訂閱事件為startLocalFa和startRemoteFa,代碼如下所示:

  1. private void showDevicesDialog() {  
  2.     new SelectDeviceDialog(this, devices, deviceInfo -> {  
  3.         startLocalFa(deviceInfo.getDeviceId());  
  4.         startRemoteFa(deviceInfo.getDeviceId());  
  5.     }).show();  

 而拼圖游戲需要和另外一臺(tái)設(shè)備建立連接,拉起另外一臺(tái)設(shè)備的拼圖頁(yè)面,此回調(diào)事件為connectRemotePa,代碼如下所示:

  1. private void showDevicesDialog() {  
  2.     new SelectDeviceDialog(this, devices, deviceInfo -> {  
  3.         connectRemotePa(deviceInfo.getDeviceId(), PictureRemoteProxy.REQUEST_START_ABILITY);  
  4.     }).show();  

 需要說明的是回調(diào)函數(shù)中deviceInfo是單擊選中的設(shè)備信息,具體實(shí)現(xiàn)在SelectDeviceDialog的initView方法中,代碼如下所示:

  1. DevicesListAdapter devicesListAdapter = new DevicesListAdapter(devices, context);  
  2. devicesListContainer.setItemProvider(devicesListAdapter);  
  3. devicesListContainer.setItemClickedListener((listContainer, component, position, l) -> {  
  4.     listener.callBack(devices.get(position));  
  5.     commonDialog.hide();  
  6. }); 

 至此,您已經(jīng)完成了多設(shè)備協(xié)同中發(fā)現(xiàn)設(shè)備和建立連接這一關(guān)鍵步驟。

說明:

實(shí)現(xiàn)遠(yuǎn)程啟動(dòng)FA,需要至少兩個(gè)設(shè)備處于同一個(gè)分布式網(wǎng)絡(luò)中,可以通過如下操作實(shí)現(xiàn):

所有設(shè)備接入同一網(wǎng)絡(luò);

所有設(shè)備登錄相同華為賬號(hào);

所有設(shè)備上開啟"設(shè)置->更多連接->多設(shè)備協(xié)同 "。

6. 早教算數(shù)題

點(diǎn)擊早教算數(shù)題,系統(tǒng)會(huì)為您隨機(jī)出一道兩位數(shù)的加法,點(diǎn)擊實(shí)時(shí)輔導(dǎo)會(huì)拉起兩個(gè)畫布,本地端可以用黑色筆跡進(jìn)行草稿運(yùn)算,遠(yuǎn)程端可以用紅色筆跡進(jìn)行實(shí)時(shí)指導(dǎo),操作步驟兩端實(shí)時(shí)同步,效果如下圖所示。接下來我們將為您詳細(xì)介紹如何利用HarmonyOS分布式技術(shù)實(shí)現(xiàn)兩端的同步繪制。

HarmonyOS 分布式親子教育-鴻蒙HarmonyOS技術(shù)社區(qū)

Step 1 - 選擇設(shè)備

詳見"5 發(fā)現(xiàn)設(shè)備和建立連接"。

Step 2 - 建立連接

點(diǎn)擊選擇設(shè)備后會(huì)進(jìn)入繪圖頁(yè)面,在onStart方法中會(huì)調(diào)用初始化并連接設(shè)備的函數(shù)initAndConnectDevice,通過intent傳參獲取遠(yuǎn)程設(shè)備的remoteDeviceId,并調(diào)用connectRemotePa進(jìn)行連接,代碼如下所示:

  1. private void initAndConnectDevice(Intent intent) {  
  2.     // 頁(yè)面初始化  
  3.     this.context = MathDrawRemSlice.this;  
  4.     String remoteDeviceId = intent.getStringParam(CommonData.KEY_REMOTE_DEVICEID);  
  5.     isLocal = intent.getBooleanParam(CommonData.KEY_IS_LOCAL, false);  
  6.     if (findComponentById(ResourceTable.Id_text_title) instanceof Text) {  
  7.         Text textTitle = (Text) findComponentById(ResourceTable.Id_text_title);  
  8.         textTitle.setText(isLocal ? "本地端" : "遠(yuǎn)程端");  
  9.     }  
  10.   
  11.     // 連接遠(yuǎn)程服務(wù)  
  12.     if (!remoteDeviceId.isEmpty()) {  
  13.         connectRemotePa(remoteDeviceId);  
  14.     } else {  
  15.         LogUtil.info(TAG, "localDeviceId is null");  
  16.     }  

 其中調(diào)用connectRemotePa方法后會(huì)和MathGameServiceAbility建立服務(wù)連接,代碼如下所示:

  1. Intent connectPaIntent = new Intent();  
  2. Operation operation = new Intent.OperationBuilder()  
  3.         .withDeviceId(deviceId)  
  4.        .withBundleName(getBundleName())  
  5.        .withAbilityName(CommonData.MATH_GAME_SERVICE_NAME)  
  6.         .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)  
  7.         .build();  
  8. connectPaIntent.setOperation(operation); 

 連接成功后會(huì)回調(diào)onAbilityConnectDone方法,此時(shí)兩臺(tái)設(shè)備即建立了連接。而后,可以調(diào)用senDataToRemote方法進(jìn)行數(shù)據(jù)發(fā)送,代碼如下所示:

  1. public void onAbilityConnectDone(ElementName elementName, IRemoteObject remote, int resultCode) {  
  2.     LogUtil.info(TAG, "onAbilityConnectDone......");  
  3.     connectAbility(elementName, remote, requestType);  
  4. }  
  5.    
  6. private void connectAbility(ElementName elementName, IRemoteObject remote, int requestType) {  
  7.     proxy = new PictureRemoteProxy(remote);  
  8.     LogUtil.error(TAG, "connectRemoteAbility done");  
  9.     if (proxy != null) {  
  10.         try {  
  11.             proxy.senDataToRemote(requestType);  
  12.         } catch (RemoteException e) {  
  13.             LogUtil.error(TAG, "onAbilityConnectDone RemoteException");  
  14.         }  
  15.     }  

 Step 3 - 繪圖操作(具體業(yè)務(wù),準(zhǔn)備要發(fā)送的數(shù)據(jù))

DrawPoint是一個(gè)繪圖的工具類,繪制一個(gè)點(diǎn)會(huì)記錄三個(gè)信息:X軸坐標(biāo)、Y軸坐標(biāo)、是否是最后一個(gè)點(diǎn)。將此信息記錄到數(shù)組pointsX、pointsY、isLastPointArray中,并封裝為L(zhǎng)ist allPoints的數(shù)據(jù)類型,定義如下:

  1. private float[] pointsX;  
  2. private float[] pointsY;  
  3. private boolean[] isLastPointArray;  
  4. private List<MyPoint> allPoints = new ArrayList<>(); 

 其中,是否為最后一個(gè)點(diǎn)是通過TouchEvent.PRIMARY_POINT_UP事件進(jìn)行區(qū)分的。當(dāng)檢測(cè)到該事件時(shí),會(huì)調(diào)用回調(diào)函數(shù)callBack.callBack(allPoints) ,代碼如下所示:

  1. if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_UP) {  
  2.     point.setLastPoint(true);  
  3.     allPoints.add(point);  
  4.     callBack.callBack(allPoints);  
  5. }  

 此時(shí)會(huì)向上回調(diào)到setOnDrawBack方法,代碼如下所示:

  1. public void setOnDrawBack(OnDrawCallBack callBack) {  
  2.     this.callBack = callBack;  

 MathDrawRemSlice中initDraw()會(huì)初始化畫布,一次繪制完成后會(huì)回調(diào)setOnDrawBack方法,其中points是上述步驟中繪制的點(diǎn),將其存入數(shù)組pointsX、pointsY、isLastPointArray中,代碼如下所示:

  1. drawl.setOnDrawBack(points -> {  
  2.     if (points != null && points.size() > 1) {  
  3.         pointsX = new float[points.size()];  
  4.         pointsY = new float[points.size()];  
  5.         isLastPoint = new boolean[points.size()];  
  6.         for (int i = 0; i < points.size(); i++) {  
  7.            pointsX[i] = points.get(i).getPositionX();  
  8.            pointsY[i] = points.get(i).getPositionY();  
  9.            isLastPoint[i] = points.get(i).isLastPoint();  
  10.         }  
  11.         ...  
  12.     }  
  13. }); 

 此時(shí)就完成了本地畫布繪制并將繪制的點(diǎn)信息存放到了數(shù)組pointsX、pointsY、isLastPointArray中,即已經(jīng)準(zhǔn)備好了要發(fā)送的數(shù)據(jù)。

Step 4 - 發(fā)送數(shù)據(jù)

發(fā)送數(shù)據(jù)涉及到Service Ability、分布式任務(wù)調(diào)度、公共事件三項(xiàng)HarmonyOS能力,如果您還不熟悉相關(guān)基礎(chǔ)知識(shí)可以先參考官方文檔進(jìn)行學(xué)習(xí)。一次繪制完成后,會(huì)調(diào)用senDataToRemote方法,將繪制的點(diǎn)信息(pointsX、pointsY、isLastPointArray)存放到data中并發(fā)送出去,代碼如下所示:

  1. private void senDataToRemote(int requestType) throws RemoteException {  
  2.     LogUtil.info(TAG, "send data to local draw service");  
  3.     MessageParcel data = MessageParcel.obtain();  
  4.     MessageParcel reply = MessageParcel.obtain();  
  5.     MessageOption option = new MessageOption(MessageOption.TF_SYNC);  
  6.     try {  
  7.         if (pointsX != null && pointsY != null && isLastPoint != null) {  
  8.            data.writeFloatArray(pointsX);  
  9.            data.writeFloatArray(pointsY);  
  10.            data.writeBooleanArray(isLastPoint);  
  11.         }  
  12.         remote.sendRequest(requestType, data, reply, option);  
  13.         int ec = reply.readInt();  
  14.         if (ec != ERR_OK) {  
  15.             LogUtil.error(TAG, "RemoteException:");  
  16.         }  
  17.     } catch (RemoteException e) {  
  18.         LogUtil.error(TAG, "RemoteException:");  
  19.     } finally {  
  20.         data.reclaim();  
  21.         reply.reclaim();  
  22.     }  

 其中,remote.sendRequest是將數(shù)據(jù)發(fā)送到MathGameServiceAbility的服務(wù)中(因?yàn)椴襟E2是和MathGameServiceAbility建立服務(wù)連接),建立服務(wù)連接后會(huì)回調(diào)onRemoteRequest方法,代碼如下所示:

  1. @Override  
  2. public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {  
  3.     LogUtil.info(TAG, "onRemoteRequest......");  
  4.     float[] pointsX = data.readFloatArray();  
  5.     float[] pointsY = data.readFloatArray();  
  6.     boolean[] isLastPointArray = data.readBooleanArray();  
  7.     reply.writeInt(ERR_OK);  
  8.     sendEvent(isLastPointArray, pointsX, pointsY);  
  9.     return true;  

 在onRemoteRequest方法中,會(huì)調(diào)用sendEvent將數(shù)組pointsX、pointsY、isLastPointArray發(fā)送出去。sendEvent是通過公共事件的方式進(jìn)行數(shù)據(jù)傳遞的,其注冊(cè)的公共事件是CommonData.MATH_DRAW_EVENT,代碼如下所示:

  1. private void sendEvent(boolean[] isLastPoint, float[] pointsX, float[] pointsY) {  
  2.     LogUtil.info(TAG, "sendEvent......");  
  3.     try {  
  4.         Intent intent = new Intent();  
  5.         Operation operation = new Intent.OperationBuilder()  
  6.                 .withAction(CommonData.MATH_DRAW_EVENT)  
  7.                 .build();  
  8.         intent.setOperation(operation);  
  9.        intent.setParam(CommonData.KEY_POINT_X, pointsX);  
  10.        intent.setParam(CommonData.KEY_POINT_Y, pointsY);  
  11.        intent.setParam(CommonData.KEY_IS_LAST_POINT, isLastPoint);  
  12.         CommonEventData eventData = new CommonEventData(intent);  
  13.         CommonEventManager.publishCommonEvent(eventData);  
  14.     } catch (RemoteException e) {  
  15.         LogUtil.error(TAG, "publishCommonEvent occur exception.");  
  16.     }  

Step 5 - 接收數(shù)據(jù)

MathDrawRemSlice中會(huì)訂閱CommonData.MATH_DRAW_EVENT的公共事件,代碼如下所示:

  1. private void subscribe() {  
  2.     MatchingSkills matchingSkills = new MatchingSkills();  
  3.     matchingSkills.addEvent(CommonData.MATH_DRAW_EVENT);  
  4.     matchingSkills.addEvent(CommonEventSupport.COMMON_EVENT_SCREEN_ON);  
  5.     CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(matchingSkills);  
  6.     subscriber = new MyCommonEventSubscriber(subscribeInfo);  
  7.     try {  
  8.         CommonEventManager.subscribeCommonEvent(subscriber);  
  9.     } catch (RemoteException e) {  
  10.         LogUtil.error("""subscribeCommonEvent occur exception.");  
  11.     }  

當(dāng)訂閱到相關(guān)事件時(shí)會(huì)回調(diào)onReceiveEvent方法,此時(shí)即可將pointsX、pointsY、isLastPointArray解析出來,然后調(diào)用drawl.setDrawParams(isLastPointArray, pointsX, pointsY)方法在遠(yuǎn)程端進(jìn)行繪制,代碼如下所示:

  1. public void onReceiveEvent(CommonEventData commonEventData) {  
  2.     Intent intent = commonEventData.getIntent();  
  3.     pointsX = intent.getFloatArrayParam(CommonData.KEY_POINT_X);  
  4.     pointsY = intent.getFloatArrayParam(CommonData.KEY_POINT_Y);  
  5.     isLastPoint = intent.getBooleanArrayParam(CommonData.KEY_IS_LAST_POINT);  
  6.     // 接收數(shù)據(jù)后,對(duì)遠(yuǎn)程端畫布進(jìn)行繪制  
  7.     drawl.setDrawParams(isLastPoint, pointsX, pointsY);  
  8.     LogUtil.info(TAG, "onReceiveEvent.....");  

Step 6 - 數(shù)據(jù)的雙向通信

步驟1-5中為您講解了數(shù)據(jù)的單向通信,即本地端向遠(yuǎn)程端的交互。本例的繪圖是雙向通信,兩端都可以進(jìn)行繪制,這是怎么做到的呢?原來在步驟1中選擇設(shè)備時(shí),會(huì)調(diào)用startLocalFa和startRemoteFa,其中startLocalFa傳出的remoteDeviceId是所選擇的設(shè)備ID,startRemoteFa傳出的remoteDeviceId是當(dāng)前設(shè)備的ID,由此實(shí)現(xiàn)了雙向通信,代碼如下所示:

  1. private void showDevicesDialog() {  
  2.     new SelectDeviceDialog(this, devices, deviceInfo -> {  
  3.         startLocalFa(deviceInfo.getDeviceId());  
  4.         startRemoteFa(deviceInfo.getDeviceId());  
  5.     }).show();  
  6. }  
  7. private void startLocalFa(String deviceId) {  
  8.     LogUtil.info(TAG, "startLocalFa......");  
  9.     Intent intent = new Intent();  
  10.     intent.setParam(CommonData.KEY_REMOTE_DEVICEID, deviceId);  
  11.     ...  
  12. }  
  13. private void startRemoteFa(String deviceId) {  
  14.     LogUtil.info(TAG, "startRemoteFa......");  
  15.     String localDeviceId = KvManagerFactory.getInstance()  
  16.             .createKvManager(new KvManagerConfig(this)).getLocalDeviceInfo().getId();  
  17.     Intent intent = new Intent();  
  18.     intent.setParam(CommonData.KEY_REMOTE_DEVICEID, localDeviceId);  
  19.     ...  

通過選擇設(shè)備、建立連接、發(fā)送數(shù)據(jù)、接收數(shù)據(jù)這幾個(gè)關(guān)鍵步驟,您就可以實(shí)現(xiàn)兩臺(tái)設(shè)備的同步繪圖、實(shí)時(shí)顯示的功能。

—-結(jié)束

7. 益智拼圖游戲

點(diǎn)擊益智拼圖游戲會(huì)拉起一個(gè)九宮格的拼圖游戲(圖片會(huì)隨機(jī)亂序),點(diǎn)擊圖片可以進(jìn)行拼圖。在本地端中點(diǎn)擊親子協(xié)同,遠(yuǎn)程端會(huì)拉起一個(gè)一模一樣的游戲頁(yè)面,兩個(gè)設(shè)備可以進(jìn)行實(shí)時(shí)互動(dòng),同步對(duì)圖片進(jìn)行拼接,操作步驟亦可實(shí)時(shí)同步,效果圖如下所示。接下來我們將為您詳細(xì)介紹如何利用HarmonyOS分布式技術(shù)實(shí)現(xiàn)一個(gè)益智拼圖游戲。

HarmonyOS 分布式親子教育-鴻蒙HarmonyOS技術(shù)社區(qū)

Step 1 - 選擇設(shè)備

詳見"5 發(fā)現(xiàn)設(shè)備和建立連接"這一章節(jié)。

Step 2 - 建立連接并拉起設(shè)備

點(diǎn)擊親子協(xié)同后會(huì)記錄選擇設(shè)備的deviceId,調(diào)用connectRemotePa方法后會(huì)和PictureGameServiceAbility建立服務(wù)連接,其中標(biāo)識(shí)位傳遞的是REQUEST_START_ABILITY,代碼如下所示:

  1. private void showDevicesDialog() {  
  2.     new SelectDeviceDialog(this, devices, deviceInfo -> {  
  3.         connectRemotePa(deviceInfo.getDeviceId(), PictureRemoteProxy.REQUEST_START_ABILITY);  
  4.     }).show();  
  5. }  
  6. private void connectRemotePa(String deviceId, int requestType) {  
  7.     if (!deviceId.isEmpty()) {  
  8.         Intent connectPaIntent = new Intent();  
  9.         Operation operation = new Intent.OperationBuilder()  
  10.                 .withDeviceId(deviceId)  
  11.                 .withBundleName(getBundleName())  
  12.                 .withAbilityName(CommonData.PICTURE_GAME_SERVICE_NAME)  
  13.                 .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)  
  14.                 .build();  
  15.         connectPaIntent.setOperation(operation);  
  16.     ...  

連接成功后會(huì)回調(diào)onAbilityConnectDone方法,此時(shí)兩臺(tái)設(shè)備即建立了連接。然后調(diào)用sendDataToRemote方法進(jìn)行數(shù)據(jù)發(fā)送,其中requestType為REQUEST_START_ABILITY,代碼如下所示:

  1. public void onAbilityConnectDone(ElementName elementName, IRemoteObject remote, int resultCode) {  
  2.     LogUtil.info(TAG, "onAbilityConnectDone......");  
  3.     connectAbility(elementName, remote, requestType);  
  4. }  
  5.    
  6. private void connectAbility(ElementName elementName, IRemoteObject remote, int requestType) {  
  7.     proxy = new PictureRemoteProxy(remote);  
  8.     LogUtil.error(TAG, "connectRemoteAbility done");  
  9.     if (proxy != null) {  
  10.         try {  
  11.             proxy.sendDataToRemote(requestType);  
  12.         } catch (RemoteException e) {  
  13.             LogUtil.error(TAG, "onAbilityConnectDone RemoteException");  
  14.         }  
  15.     }  

PictureGameServiceAbility服務(wù)中接收到了senDataToRemote的消息后會(huì)回調(diào)onRemoteRequest,因?yàn)榇藭r(shí)code為REQUEST_START_ABILITY,所以會(huì)拉起遠(yuǎn)程端的拼圖頁(yè)面,代碼如下所示:

  1. public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {  
  2.     ...  
  3.     if (code == REQUEST_START_ABILITY) {  
  4.         LogUtil.error(TAG, "RemoteServiceAbility::isFirstStart:");  
  5.         Intent secondIntent = new Intent();  
  6.         Operation operation = new Intent.OperationBuilder().withDeviceId("")  
  7.                 .withBundleName(getBundleName())  
  8.                 .withAbilityName(CommonData.ABILITY_MAIN)  
  9.                 .withAction(CommonData.PICTURE_PAGE)  
  10.                 .build();  
  11.     ...  
  12.     } else {  
  13.     ...  
  14.     }  
  15.     ...  

以上即完成了本地端到遠(yuǎn)程端的單向通信。遠(yuǎn)程端設(shè)備拉起后,會(huì)在PictureGameAbilitySlice執(zhí)行onStart方法,進(jìn)而執(zhí)行initRemoteView方法,其中會(huì)再次調(diào)用connectRemotePa方法,此時(shí)傳遞的標(biāo)識(shí)位為REQUEST_SEND_DATA,不會(huì)重復(fù)拉起本地端設(shè)備的頁(yè)面,只會(huì)建立數(shù)據(jù)連接,代碼如下所示:

  1. private void initRemoteView(Intent intent) {  
  2.     if (!isLocal) {  
  3.         remoteDeviceId = intent.getStringParam(CommonData.KEY_REMOTE_DEVICEID);  
  4.         connectRemotePa(remoteDeviceId, PictureRemoteProxy.REQUEST_SEND_DATA);  
  5.         if (imageIndex != null) {  
  6.             updateDataInfo(intent);  
  7.         }  
  8.     }  

如上步驟即完成了本地端和遠(yuǎn)程端的雙向通信,實(shí)現(xiàn)了數(shù)據(jù)互傳的功能。

Step 3 - 拼圖操作(具體業(yè)務(wù),準(zhǔn)備要發(fā)送的數(shù)據(jù))

因篇幅有限且拼圖游戲不屬于本篇Codelab想為您重點(diǎn)介紹的HarmonyOS特性,因此不再贅述,請(qǐng)讀者自行理解。我們告訴您的結(jié)論是,完成一次拼圖操作需要記錄三個(gè)關(guān)鍵數(shù)據(jù):移動(dòng)圖片的下標(biāo)moveImageId,移動(dòng)圖片的位置movePosition和最終排列的圖片下標(biāo)imageIndex。點(diǎn)擊一次拼圖,我們就會(huì)記錄以上三個(gè)數(shù)據(jù),代碼如下所示:

  1. private class ImageClick implements Component.ClickedListener {  
  2.     @Override  
  3.     public void onClick(Component component) {  
  4.         int imageId = component.getId();  
  5.         for (int position = 0; position < imageIndex.length; position++) {  
  6.             if (imageId == imageResourceTable[position]) {  
  7.                 // 完成圖片移動(dòng),并記錄移動(dòng)信息  
  8.                 moveFun(imageId, position);  
  9.                 moveImageId = imageId;  
  10.                 movePosition = position;  
  11.             }  
  12.         }  
  13.         // 刷新頁(yè)面顯示  
  14.         setImageAndDecodeBounds(imageIndex);  
  15.         // 發(fā)送數(shù)據(jù)  
  16.         senDataToRemoteFun();  
  17.     }  

Step 4 - 發(fā)送數(shù)據(jù)

發(fā)送數(shù)據(jù)的流程和早教算數(shù)題一樣,我們?cè)俅螢槟鲈敿?xì)介紹,您可參考早教算數(shù)題進(jìn)行對(duì)照學(xué)習(xí)。一次拼圖完成后,會(huì)調(diào)用senDataToRemote方法,將關(guān)鍵信息(imageIndex、moveImageId、movePosition)存放到data中并發(fā)送出去,代碼如下所示:

  1. private void senDataToRemote(int requestType) throws RemoteException {  
  2.     MessageParcel data = MessageParcel.obtain();  
  3.      ...  
  4.     try {  
  5.      ...  
  6.         data.writeIntArray(imageIndex);  
  7.         data.writeInt(moveImageId);  
  8.         data.writeInt(movePosition);  
  9.         remote.sendRequest(requestType, data, reply, option);  
  10.      ...  
  11.     } catch (RemoteException e) {  
  12.      ...  
  13.     } finally {  
  14.      ...  
  15.     }  

其中,remote.sendRequest是將數(shù)據(jù)發(fā)送到PictureGameServiceAbility的服務(wù)中(因?yàn)椴襟E2是和PictureGameServiceAbility建立服務(wù)連接),建立服務(wù)連接后會(huì)回調(diào)onRemoteRequest方法。接收順序應(yīng)該和發(fā)送順序一致(imageIndex、moveImageId、movePosition),否則會(huì)操作接收錯(cuò)誤的情況,代碼如下所示:

  1. public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {  
  2.     LogUtil.info(TAG, "onRemoteRequest......");  
  3.     int[] imageIndex = data.readIntArray();  
  4.     int moveImageId = data.readInt();  
  5.     int movePosition = data.readInt();  
  6.      ...  
  7.     LogUtil.info(TAG, "receive number:" + imageIndex.length);  
  8.     reply.writeInt(ERR_OK);  
  9.     if (code == REQUEST_START_ABILITY) {  
  10.      ...  
  11.     } else {  
  12.         sendEvent(imageIndex, moveImageId, movePosition);  
  13.     }  
  14.     return true;  

在onRemoteRequest方法中,會(huì)調(diào)用sendEvent將imageIndex、moveImageId、movePosition發(fā)送出去。sendEvent是通過注冊(cè)公共事件的方式進(jìn)行數(shù)據(jù)傳遞的,其注冊(cè)的公共事件是CommonData. PICTURE_GAME_EVENT,代碼如下所示:

  1. private void sendEvent(int[] imageIndex, int moveImageId, int movePosition) {  
  2.     try {  
  3.         Intent intent = new Intent();  
  4.         Operation operation = new Intent.OperationBuilder()  
  5.                 .withAction(CommonData.PICTURE_GAME_EVENT)  
  6.                 .build();  
  7.         intent.setOperation(operation);  
  8.         intent.setParam(CommonData.KEY_IMAGE_INDEX, imageIndex);  
  9.         intent.setParam(CommonData.KEY_MOVE_IMAGE_ID, moveImageId);  
  10.         intent.setParam(CommonData.KEY_MOVE_POSITION, movePosition);  
  11.         CommonEventData eventData = new CommonEventData(intent);  
  12.         CommonEventManager.publishCommonEvent(eventData);  
  13.     } catch (RemoteException e) {  
  14.         LogUtil.error(TAG, "publishCommonEvent occur exception.");  
  15.     }  

Step 5 - 接收數(shù)據(jù)

接收數(shù)據(jù)的代碼流程和早教算數(shù)題一樣,我們?cè)俅螢槟鲈敿?xì)介紹,您可參考早教算數(shù)題進(jìn)行對(duì)照學(xué)習(xí)。PictureGameAbilitySlice會(huì)訂閱CommonData. PICTURE_GAME_EVENT的公共事件,代碼如下所示:

  1. private void subscribe() {  
  2.     MatchingSkills matchingSkills = new MatchingSkills();  
  3.     matchingSkills.addEvent(CommonData.PICTURE_GAME_EVENT);  
  4.     matchingSkills.addEvent(CommonEventSupport.COMMON_EVENT_SCREEN_ON);  
  5.     CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(matchingSkills);  
  6.     subscriber = new MyCommonEventSubscriber(subscribeInfo);  
  7.     try {  
  8.         CommonEventManager.subscribeCommonEvent(subscriber);  
  9.     } catch (RemoteException e) {  
  10.         LogUtil.error("""subscribeCommonEvent occur exception.");  
  11.     }  

當(dāng)訂閱到相關(guān)事件時(shí)會(huì)回調(diào)onReceiveEvent方法,此時(shí)即可將imageIndex、moveImageId、movePosition解析出來,并調(diào)用updateDataInfo更新對(duì)端的布局文件,代碼如下所示:

  1. @Override  
  2. public void onReceiveEvent(CommonEventData commonEventData) {  
  3.     ...  
  4.     Intent intent = commonEventData.getIntent();  
  5.     updateDataInfo(intent);  
  6. }  
  7.    
  8. private void updateDataInfo(Intent intent) {  
  9.     imageIndex = intent.getIntArrayParam(CommonData.KEY_IMAGE_INDEX);  
  10.     moveImageId = intent.getIntParam(CommonData.KEY_MOVE_IMAGE_ID, -1);  
  11.     movePosition = intent.getIntParam(CommonData.KEY_MOVE_POSITION, -1);  
  12.     getUITaskDispatcher().delayDispatch(() -> setImageAndDecodeBounds(imageIndex), DELAY_TIME);  

通過選擇設(shè)備、建立連接、發(fā)送數(shù)據(jù)、接收數(shù)據(jù)這幾個(gè)關(guān)鍵步驟,您就可以實(shí)現(xiàn)兩臺(tái)設(shè)備的同步拼圖的功能。以上兩個(gè)案例,我們完整的學(xué)習(xí)了兩臺(tái)設(shè)備之間的數(shù)據(jù)交互,體驗(yàn)了HarmonyOS分布式特性,相信您一定有所收獲。

—-結(jié)束

說明:

以上代碼僅demo演示參考使用,產(chǎn)品化的代碼需要考慮數(shù)據(jù)校驗(yàn)和國(guó)際化。

8. 回顧和總結(jié)

本篇Codelab通過一個(gè)親子早教系統(tǒng),完整的為您介紹了早教算數(shù)題和益智拼圖游戲兩個(gè)綜合案例,旨在幫助您快速了解HarmonyOS應(yīng)用開發(fā)、多屏互動(dòng)、分布式跨設(shè)備協(xié)同的功能了解。您需要重點(diǎn)掌握跨設(shè)備協(xié)同、分布式任務(wù)調(diào)度、公共事件三項(xiàng)HarmonyOS能力。特別的,我們通過拆解步驟的方式詳細(xì)為您介紹了如何在兩臺(tái)設(shè)備之間進(jìn)行數(shù)據(jù)傳遞,這是您需要重點(diǎn)學(xué)習(xí)和掌握的知識(shí)點(diǎn)。

另外,本篇Codelab的兩個(gè)案例還可以通過分布式數(shù)據(jù)服務(wù)和分布式文件服務(wù)去優(yōu)化代碼,我們將在后續(xù)Codelab中為您介紹這方面的知識(shí)點(diǎn)和案例。

9. 恭喜您

  • 目前您已經(jīng)成功完成了Codelab并且學(xué)到了:
  • 常用布局、自定義控件的使用
  • Page Ability、Service Ability、Intent
  • 多屏互動(dòng)、分布式跨設(shè)備協(xié)同、分布式任務(wù)調(diào)度
  • 公共事件

10. 參考

gitee源碼

github源碼

想了解更多內(nèi)容,請(qǐng)?jiān)L問:

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos.51cto.com

 

責(zé)任編輯:jianghua 來源: 鴻蒙社區(qū)
相關(guān)推薦

2021-05-28 09:52:00

鴻蒙HarmonyOS應(yīng)用

2019-10-10 09:16:34

Zookeeper架構(gòu)分布式

2020-11-06 12:12:35

HarmonyOS

2023-05-29 14:07:00

Zuul網(wǎng)關(guān)系統(tǒng)

2019-06-19 15:40:06

分布式鎖RedisJava

2017-09-01 05:35:58

分布式計(jì)算存儲(chǔ)

2021-07-23 08:57:32

鴻蒙HarmonyOS應(yīng)用

2017-10-27 08:40:44

分布式存儲(chǔ)剪枝系統(tǒng)

2023-10-26 18:10:43

分布式并行技術(shù)系統(tǒng)

2024-03-01 09:53:34

2018-07-17 08:14:22

分布式分布式鎖方位

2023-05-12 08:23:03

分布式系統(tǒng)網(wǎng)絡(luò)

2022-06-27 08:21:05

Seata分布式事務(wù)微服務(wù)

2020-09-29 19:20:05

鴻蒙

2011-03-28 13:39:45

nagios分布式

2023-02-11 00:04:17

分布式系統(tǒng)安全

2022-06-21 08:27:22

Seata分布式事務(wù)

2022-10-25 14:05:47

共識(shí)算法系統(tǒng)

2017-07-26 15:08:05

大數(shù)據(jù)分布式事務(wù)

2024-01-10 08:02:03

分布式技術(shù)令牌,
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

在线播放中文一区| 中文字幕亚洲不卡| 国产精品美女免费| 91高清免费观看| 九九热hot精品视频在线播放| 午夜精品久久久久久久| 亚洲 日韩 国产第一区| 亚洲国产剧情在线观看| 久久不射网站| 欧美日韩成人在线视频| 国产一二三四区在线| 中文在线免费一区三区| 欧美中文字幕一二三区视频| 狠狠精品干练久久久无码中文字幕| 日本人妖在线| 国产精品一级在线| 国产精品美女久久久久久免费 | 亚洲国产精品v| 风间由美久久久| 国产成人a v| 亚洲久久成人| 欧美激情一区二区三区在线视频观看 | 国产丰满果冻videossex| 视频一区二区国产| 性欧美xxxx| 极品魔鬼身材女神啪啪精品| 国产欧美日韩精品一区二区免费 | 欧美电影一区二区三区| 激情综合网婷婷| 成人性生交大片免费看网站| 成人免费在线视频| 亚洲图片都市激情| 黄色片视频在线观看| 成人久久18免费网站麻豆| 91美女片黄在线观| 亚洲一级在线播放| 免费xxxx性欧美18vr| 国产精品成av人在线视午夜片| 亚洲精品视频在线观看免费视频| 欧美涩涩视频| 欧美伦理91i| 亚洲成人生活片| 亚洲免费二区| 久久视频在线播放| 艳妇荡乳欲伦69影片| 欧美韩日高清| 日韩视频精品在线| 我要看一级黄色录像| 成人羞羞在线观看网站| 亚洲欧美综合图区| 熟女俱乐部一区二区| 伊人春色之综合网| 亚洲欧美激情四射在线日| 视频免费在线观看| 青青草久久爱| 亚洲乱码一区二区| 91网站免费入口| 精品一二三区| 丝袜美腿精品国产二区| 国产三级在线观看完整版| 人人狠狠综合久久亚洲婷| 一区二区三区四区在线观看视频 | 成人欧美一区二区三区黑人麻豆| 宅男一区二区三区| 最新黄网在线观看| 夜夜嗨av一区二区三区中文字幕| www.一区二区.com| 性xxxxfreexxxxx欧美丶| 色先锋aa成人| 日韩精品视频一二三| 国产激情精品一区二区三区| 日韩小视频在线观看专区| 一本之道在线视频| 国产伦精品一区二区三区在线播放| 亚洲第一精品福利| 波多野结衣一本| 日韩精品午夜| 久久97精品久久久久久久不卡| 久久久久97国产| 久久亚洲精品伦理| 成人激情视频小说免费下载| 精品国产伦一区二区三区| 波多野结衣91| 西游记1978| www中文字幕在线观看| 精品国产31久久久久久| 日本熟妇人妻中出| 欧美专区视频| 亚洲美女av在线| 亚洲区一区二区三| 日韩网站在线| 国产欧美一区二区三区在线看| a级片免费视频| 久久视频一区二区| 99国产精品白浆在线观看免费| 性欧美xxx69hd高清| 777久久久精品| 菠萝菠萝蜜网站| 日韩欧美伦理| 97精品国产97久久久久久春色| 欧美日韩 一区二区三区| 国产精品夜夜嗨| 日本最新一区二区三区视频观看| 黄a在线观看| 色播五月激情综合网| 夜夜爽久久精品91| 欧美一区二区麻豆红桃视频| 欧美极品欧美精品欧美视频| 伊人精品在线视频| 91性感美女视频| 欧美日韩一区二区三区电影| 成人欧美magnet| 日韩精品一区二区三区在线观看| 久久国产柳州莫菁门| 影音先锋久久久| 91九色国产在线| youjizz在线播放| 欧美日韩人人澡狠狠躁视频| 91丝袜超薄交口足| 日韩电影二区| 国产成人亚洲精品| 亚洲色图狠狠干| 亚洲成人一二三| wwwxxxx在线观看| 五月天久久777| 国产精品久久久久久久久久久久久| 刘亦菲久久免费一区二区| 亚洲欧洲日韩av| 亚洲综合婷婷久久| 欧美丝袜一区| 国产成人中文字幕| 每日更新av在线播放| 精品电影在线观看| 成人性生活免费看| 最新日韩欧美| 国产一区二区高清不卡 | 国产91精品久久久久久久| 亚洲精品一区二区三区区别| 日韩毛片视频在线看| 高潮一区二区三区| 色88久久久久高潮综合影院| 国产97在线视频| 九色视频在线观看免费播放| 欧美午夜视频一区二区| 成人免费无码大片a毛片| 欧美性久久久| 国产精品我不卡| 国产经典三级在线| 亚洲国产成人久久综合| 日本一级黄色录像| 91丝袜呻吟高潮美腿白嫩在线观看| 无码粉嫩虎白一线天在线观看 | 欧美人妻精品一区二区三区 | 国产精品主播| 欧美成ee人免费视频| 免费看av不卡| 中文字幕在线看视频国产欧美| 中文在线观看av| 中文字幕一区二区三区在线播放 | 久久久久久成人精品| 老牛影视av牛牛影视av| 丰满岳妇乱一区二区三区| 少妇毛片一区二区三区| 久久综合激情| 亚洲最大色综合成人av| 国产美女亚洲精品7777| 欧美高清电影在线看| 三级小视频在线观看| 色悠久久久久综合欧美99| 免费看的黄色录像| 国产精品99久久久久| 免费av手机在线观看| 九九久久电影| 91视频九色网站| av资源新版天堂在线| 亚洲性夜色噜噜噜7777| 97人妻精品一区二区三区视频| 一区二区三区成人| 中文字字幕码一二三区| 精品一区二区三区不卡 | 青青草精品视频| 成人在线观看www| 日本在线中文字幕一区| 国产精品最新在线观看| 亚洲小说区图片| 亚洲男人av电影| 国产又黄又粗又长| 亚洲成人动漫在线观看| 国产黄色录像视频| 盗摄精品av一区二区三区| www.xxx亚洲| 欧美fxxxxxx另类| 欧美日韩精品不卡| 亚洲精品黑牛一区二区三区| 热久久这里只有精品| 黄网站app在线观看| 日韩精品视频在线观看免费| 亚洲综合精品国产一区二区三区 | 欧美性受xxx黑人xyx性爽| 亚洲精品菠萝久久久久久久| 免费污网站在线观看| 成人激情小说乱人伦| 免费看涩涩视频| 亚洲免费中文| 国产肉体ⅹxxx137大胆| 色爱综合网欧美| 欧美极品日韩| 久久夜色电影| 亚洲永久在线观看| 中韩乱幕日产无线码一区| 97超碰国产精品女人人人爽| 欧美激情免费| 亚洲新声在线观看| 色综合成人av| 精品国产一区二区三区不卡| 中文字幕日韩三级| 日本高清免费不卡视频| 国产九色在线播放九色| 亚洲综合在线视频| 免费国产羞羞网站美图| 欧美极品aⅴ影院| 波多野结衣办公室33分钟| www.色综合.com| 4438x全国最大成人| 激情综合色丁香一区二区| 噼里啪啦国语在线观看免费版高清版| 国产亚洲福利| avav在线播放| 国产一区二区三区自拍| 50度灰在线观看| 婷婷综合久久| 欧美aaa在线观看| 欧美电影一区| 亚洲欧洲国产精品久久| 精品国产乱码久久久久久蜜坠欲下 | 欧美丰满少妇xxxx| av观看在线| 久久色免费在线视频| 好了av在线| 久久手机精品视频| av网站免费在线观看| 久久视频国产精品免费视频在线| 日本电影在线观看网站| 色偷偷偷亚洲综合网另类| 91xxx在线观看| 日韩在线视频二区| 精品欧美色视频网站在线观看| 日韩中文视频免费在线观看| jizz日韩| www.国产一区| 97超碰资源站在线观看| 欧美华人在线视频| 久久不射影院| 欧美亚洲在线视频| 成人黄色免费短视频| 国产精品黄视频| 香蕉久久一区| 99re在线国产| 日本亚洲不卡| 亚洲不卡一卡2卡三卡4卡5卡精品| 精品在线观看入口| 一区二区三区国| 欧美精品三区| 免费无遮挡无码永久视频| 日一区二区三区| 亚洲怡红院在线| 国产91精品在线观看| 日韩网站在线播放| 国产精品少妇自拍| 极品盗摄国产盗摄合集| 红桃av永久久久| 91在线视频免费播放| 欧美日韩国产在线观看| 国产成人av免费看| 亚洲国内高清视频| 成全电影播放在线观看国语| 另类视频在线观看| 亚洲一区站长工具| 成人网在线免费看| 欧美a一欧美| 亚洲 国产 日韩 综合一区| 欧美特黄a级高清免费大片a级| 免费在线观看亚洲视频| 美女国产一区二区| 男人网站在线观看| 中文幕一区二区三区久久蜜桃| 黄视频网站免费看| 天天综合色天天综合色h| 中文字幕一区二区久久人妻| 日韩视频永久免费| 国产原创av在线| 欧美激情三级免费| 国产极品久久久久久久久波多结野| 91久久精品www人人做人人爽| 欧美顶级毛片在线播放| 中文字幕成人一区| 久久一区二区三区四区五区| 性鲍视频在线观看| 欧美激情在线一区二区| 五月天婷婷丁香| 欧美精品日韩精品| 男人的天堂在线视频| 欧美日韩国产成人| 日韩综合久久| 欧美日韩国产免费一区二区三区| 欧美激情日韩| jizz18女人| 久久久国产精品不卡| 国产一级视频在线| 欧美电影一区二区三区| 九色在线观看| 国产69久久精品成人| 我要色综合中文字幕| 中文字幕免费在线不卡| 日韩激情视频在线观看| 精品无码国产一区二区三区51安| 亚洲色图欧洲色图婷婷| 国产精品欧美综合| 国产亚洲成精品久久| 国产高清中文字幕在线| 超碰97网站| 亚洲国产一区二区三区在线播放 | 影音成人av| 美乳视频一区二区| 一本久道久久综合婷婷鲸鱼| 国产伦理在线观看| 亚洲欧美激情插| 亚洲最大成人av| 色妞在线综合亚洲欧美| 日韩欧美精品一区二区综合视频| 美女亚洲精品| 久久精品伊人| 91成人破解版| 欧洲激情一区二区| av亚洲在线| 国产精品影片在线观看| 欧美天天综合| 免费看污黄网站| 中文久久乱码一区二区| 亚洲综合五月天婷婷丁香| 少妇高潮 亚洲精品| 欧美少妇激情| 国产免费一区二区三区四在线播放 | 日韩一区在线看| 国产成人精品无码高潮| 精品中文字幕在线| 亚洲一区二区三区四区电影 | 精品精品国产高清a毛片牛牛| 性爱视频在线播放| 国产精品日韩欧美一区二区| 在线欧美不卡| 最新中文字幕视频| 91福利在线看| 麻豆影视在线观看_| 亚洲aⅴ日韩av电影在线观看| 自由日本语亚洲人高潮| 稀缺呦国内精品呦| 色综合天天综合色综合av| 黄色软件在线| 91久久久久久久久久久久久| 午夜精品久久久久99热蜜桃导演| 亚洲av无码成人精品区| 精品久久久久久中文字幕| 免费在线性爱视频| 国产有码在线一区二区视频| 亚洲影视一区二区三区| 国产精品成人99一区无码| 色婷婷亚洲婷婷| 免费看a在线观看| 国产精品我不卡| 日日夜夜精品视频免费| 国产精品99久久久久久成人| 精品国产精品网麻豆系列| 成人欧美magnet| 在线视频一区观看| 成人一区在线观看| www.五月婷婷.com| 久久97久久97精品免视看 | 7777在线视频| 99亚偷拍自图区亚洲| 中文字幕欧美色图| 欧美激情一区二区久久久| 沈樵精品国产成av片| 日本高清免费观看| 色婷婷精品久久二区二区蜜臀av| 18在线观看的| 日韩免费中文专区| 成人禁用看黄a在线| 怡红院男人天堂| 韩剧1988在线观看免费完整版| 日韩av在线播放网址| 国产精品久久不卡| 欧美精品在线视频| 日韩影院在线| 日韩video| 国产亚洲成年网址在线观看| 性欧美一区二区三区| 国产精品扒开腿做爽爽爽男男| 国产一区日韩一区| 99热在线观看精品|