滿滿乾貨!手把手教你實現基於eTS的分散式計算器

来源:https://www.cnblogs.com/HarmonyOSDev/archive/2022/05/23/16302402.html
-Advertisement-
Play Games

本期我們將帶來“分散式計算器”的開發,幫助大家瞭解聲明式開發範式的UI描述、組件化機制、UI狀態管理、渲染控制語法等核心機制和功能。 ...


最近收到很多小伙伴反饋,想基於擴展的TS語言(eTS)進行HarmonyOS應用開發,但是不知道代碼該從何處寫起,從0到1的過程讓新手們抓狂。

 

本期我們將帶來“分散式計算器”的開發,幫助大家瞭解聲明式開發範式的UI描述、組件化機制、UI狀態管理、渲染控制語法等核心機制和功能。下麵我們直接進入正題。

 

一、整體介紹


分散式計算器可以進行簡單的數值計算,並支持遠程拉起另一個計算器FA,實現兩個FA進行協同計算。

 

 

如圖1所示,分散式計算器界面主要由“鍵盤”、“顯示”及“標題欄”三個模塊組成。其中,“鍵盤”與“顯示”模塊負責響應用戶點擊並控制運算表達式及運算結果的顯示,實現了基礎的計算功能。“菜單欄”模塊為計算器頂部的菜單欄,是分散式計算功能的入口。

 

那麼,如何實現分散式計算器各模塊的功能?下麵我們將從組件化、聲明式描述和狀態管理三個維度來解析分散式計算器的實現。


圖1  計算器界面

1. 組件化

 

ArkUI開發框架定義了一些具有特殊含義的組件管理裝飾器,如圖2所示: 

圖2 組件管理裝飾器


根據聲明式UI的組件化思想,我們可以將通過組件管理裝飾器將計算器界面上的各個模塊組件化為一個個獨立的UI單元。 

 

2. 聲明式描述

 

通過ArkUI開發框架提供的一系列基礎組件,如Column、Text、Divider、Button等,以聲明方式進行組合和擴展來對各個模塊進行描述,包括參數構造配置、屬性配置、事件配置以及子組件配置等,並通過基礎的數據綁定和事件處理機制實現各個模塊的邏輯交互。


3. 狀態管理

 

ArkUI開發框架定義了一些具有特殊含義的狀態管理裝飾器,如圖3所示:

 

圖3 狀態管理裝飾器

通過狀態管理裝飾器裝飾組件擁有的狀態屬性,當裝飾的變數更改時,組件會重新渲染更新UI界面。

 

以上就是實現分散式計算器的核心原理,下麵我們將為大家帶來分散式計算器的基礎計算功能與分散式功能的具體實現。

 

二、基礎計算功能的實現

 

上文中提到,分散式計算器的基礎計算功能由鍵盤模塊及顯示模塊實現。

 

1. 鍵盤模塊

 

鍵盤模塊響應了用戶的點擊,並實現了計算器的基本功能。下麵我們將介紹鍵盤佈局以及鍵盤功能的實現。


(1) 鍵盤佈局

 

計算器界面上的鍵盤,其實是一張張圖片按照 4*5格式排列,如圖4所示:

圖4 鍵盤模塊

 

首先,我們需要將所有圖片保存至項目的media文件夾下,並初始化為ImageList,代碼如下:

 

export function obtainImgVertical(): Array<Array<ImageList>> {
  let list =
    [
      [
        { img: $r('app.media.ic_cal_seven'), value: '7' },
        { img: $r('app.media.ic_cal_eight'), value: '8' },
        { img: $r('app.media.ic_cal_nine'), value: '9' }
      ],
      [
        { img: $r('app.media.ic_cal_four'), value: '4' },
        { img: $r('app.media.ic_cal_five'), value: '5' },
        { img: $r('app.media.ic_cal_six'), value: '6' }
      ],
    ]
  return list
}
export function obtainImgV(): Array<ImageList> {
  let list =
    [
      { img: $r('app.media.ic_cal_delete'), value: '' },
      { img: $r('app.media.ic_cal_minus'), value: '-' },
      { img: $r('app.media.ic_cal_plus'), value: '+'  },
      { img: $r('app.media.ic_cal_equal'), value: '=' }
    ]
  return list
}

 

然後,我們需要對鍵盤模塊進行組件化操作。這裡我們通過@Component裝飾器讓鍵盤模塊成為一個獨立的組件。

 

最後,使用ArkUI開發框架提供的內置組件及屬性方法進行聲明性描述。這裡我們使用了Grid組件進行佈局,並通過ForEach組件來迭代圖片數組實現迴圈渲染,同時還為每張圖片添加了ClickButton事件方法。代碼如下:

 

@Component
export struct ButtonComponent {
  private isLand: boolean
  private onInputValue: (result) => void
  build() {
    Row() {
      Grid() {
        ForEach(obtainImgV(), (item, index) => {
          GridItem() {
            Image(item.Img)
              .margin({ top: 5 })
              .onClick(() => {
                this.onInputValue(item.value)
              })
          }
          .rowStart(index)
          .rowEnd(index === 3 ? index + 1 : index)
          .columnStart(3)
          .columnEnd(3)
        })
        ForEach(obtainImgVertical(), (item) => {
          ForEach(item, (item) => {
            GridItem() {
              Image(item.Img)
                .margin({ top: 5 })
                .onClick(() => {
                  this.onInputValue(item.value)
                })
            }
          })
        })
      }
    }
  }
}


(2) 功能實現

 

按鍵功能包含了“歸零”、“清除”、“計算”三個功能。


① 當用戶點擊“C”按鈕後,運算表達式與運算結果“歸零”,代碼如下:

 

onInputValue = (value) => {
  if (value === 'C') { // 當用戶點擊C按鈕,表達式和運算結果歸0
    this.expression = ''
    this.result = ''
    return
  }
  // 輸入數字,表達式直接拼接,計算運算結果
  this.expression += value
  this.result = JSON.stringify(MATH.evaluate(this.expression))
}

 

② 當用戶點擊“X”按鈕後,刪除運算表達式的最後一個字元。代碼如下:

 

onInputValue = (value) => {
  if (value === '') { // 當用戶點擊刪除按鈕,表達式刪除上一次的輸入,重新運算表達式
    this.expression = this.expression.substr(0, this.expression.length - 1)
    this.result = JSON.stringify(MATH.evaluate(this.expression))
    return
  }
  if (this.isOperator(value)) { // 當用戶輸入的是運算符
    // 判斷表達式最後一個字元是運算符則覆蓋上一個運算符,否則表達式直接拼接
    if (this.isOperator(this.expression.substr(this.expression.length - 1, this.expression.length))) {
      this.expression = this.expression.substr(0, this.expression.length - 1)
      this.expression += value
    } else {
      this.expression += value
    }
    return
  }
  // 輸入數字,表達式直接拼接,計算運算結果
  this.expression += value
  this.result = JSON.stringify(MATH.evaluate(this.expression))
}


③ 當用戶點擊“=”按鈕後,將調用JavaScript的math.js庫對錶達式進行計算。代碼如下:

 

import { create, all } from 'mathjs'
onInputValue = (value) => {
  if (value === '=') { // 當用戶點擊=按鈕
    this.result = ''
    // 判斷表達式最後一個字元是運算符,運算結果需要去掉最後一個運算符運算,否則直接運算
    if (this.isOperator(this.expression.substr(this.expression.length - 1, this.expression.length))) {
      this.expression = JSON.stringify(MATH.evaluate(this.expression.substr(0, this.expression.length - 1)))
    } else {
      this.expression = JSON.stringify(MATH.evaluate(this.expression))
    }
    return
  }
  // 輸入數字,表達式直接拼接,計算運算結果
  this.expression += value
  this.result = JSON.stringify(MATH.evaluate(this.expression))
}


註:計算功能的實現依賴於JavaScript的math.js庫,使用前需通過npm install mathjs--save命令下載並安裝math.js庫。

 

2. 顯示模塊

 

顯示模塊實現了“鍵入的運算表達式”與“運算結果”的顯示,本質上是Text文本,如圖5所示:

圖5 顯示模塊

 

首先我們通過@Component裝飾器使該模塊具有組件化能力,然後在build方法里描述UI結構,最後使用@Link狀態裝飾器管理組件內部的狀態數據,當這些狀態數據被修改時,將會調用所在組件的build方法進行UI刷新。代碼如下: 

 

@Component
export struct InPutComponent {
  private isLand: boolean
  @Link result: string
  @Link expression: string
  build() {
    Stack({ alignContent: this.isLand ? Alignment.BottomStart : Alignment.TopEnd }) {
      Column() {
        //運算表達式文本框
        Scroll() {
          Text(this.expression)
            .maxLines(1)
            .opacity(0.9)
            .fontWeight(400)
            .textAlign(TextAlign.Start)
            .fontSize(this.isLand ? 50 : 35)
        }
        .width('90%')
        .scrollable(ScrollDirection.Horizontal)
        .align(this.isLand ? Alignment.Start : Alignment.End)
        //運算結果文本框
        Scroll() {
          Text(this.result)
            .maxLines(1)
            .opacity(0.38)
            .textAlign(TextAlign.Start)
            .fontSize(this.isLand ? 45 : 30)
            .margin(this.isLand ? { bottom: 64 } : {})
        }
      }
    }
  }
}


至此,一個初具計算功能的計算器就實現了。下麵我們將實現計算器的分散式功能。

 

三、分散式功能的實現

 

計算器的分散式功能以菜單欄模塊為入口,並基於分散式設備管理與分散式數據管理技術實現。

 

1. 菜單欄模塊

 

“菜單欄”模塊為計算器頂部菜單欄,是計算器分散式功能的入口。

圖6 菜單欄模塊

 

如圖6所示,當用戶點擊圖標 時,執行terminate()方法,退出計算器應用。當用戶點擊 按鈕時,執行showDialog()方法,界面上彈出的分散式設備列表彈窗,選擇設備後將獲取分散式數據管理的許可權,最後實現遠端設備的拉起。代碼如下:

 

@Component
export struct TitleBar {
  build() {
    Row() {
      Image($r("app.media.ic_back"))
        .height('60%')
        .margin({ left: 32 })
        .width(this.isLand ? '5%' : '8%')
        .objectFit(ImageFit.Contain)
        //執行terminate()方法,退出計算器應用
        .onClick(() => {
          app.terminate()
        })
      Blank().layoutWeight(1)
      Image($r("app.media.ic_hop"))
        .height('60%')
        .margin({ right: 32 })
        .width(this.isLand ? '5%' : '8%')
        .objectFit(ImageFit.Contain)
        //執行showDialog()方法,界面上彈出的分散式設備列表彈窗
        .onClick(() => {
          this.showDiainfo()
        })
    }
    .width('100%')
    .height(this.isLand ? '10%' : '8%')
    .constraintSize({ minHeight: 50 })
    .alignItems(VerticalAlign.Center)
  }
}


2. 分散式設備管理

 

在分散式計算器應用中,分散式設備管理包含了分散式設備搜索、分散式設備列表彈窗、遠端設備拉起三部分。首先在分散式組網內搜索設備,然後把設備展示到分散式設備列表彈窗中,最後根據用戶的選擇拉起遠端設備。


(1) 分散式設備搜索

 

通過SUBSCRIBE_ID搜索分散式組網內的遠端設備,代碼如下:

 

startDeviceDiscovery() {
  SUBSCRIBE_ID = Math.floor(65536 * Math.random())
  let info = {
    subscribeId: SUBSCRIBE_ID,
    mode: 0xAA,
    medium: 2,
    freq: 2,
    isSameAccount: false,
    isWakeRemote: true,
    capability: 0
  }
  Logger.info(TAG, `startDeviceDiscovery ${SUBSCRIBE_ID}`)
  this.deviceManager.startDeviceDiscovery(info)
}


(2) 分散式設備列表彈窗

 

分散式設備列表彈窗實現了遠端設備的選擇,如圖7所示,用戶可以根據設備名稱選擇相應的設備進行協同計算。

 

圖7 分散式設備列表彈窗

這裡我們使用@CustomDialog裝飾器來裝飾分散式設備列表彈窗,代碼如下:

 

@CustomDialog
export struct DeviceDialog {
  build() {
    Column() {
      List() {
        ForEach(this.deviceList, (item, index) => {
          ListItem() {
            Row() {
              Text(item.deviceName)
                .fontSize(21)
                .width('90%')
                .fontColor(Color.Black)
              Image(index === this.selectedIndex ? $r('app.media.checked') : $r('app.media.uncheck'))
                .width('8%')
                .objectFit(ImageFit.Contain)
            }
            .height(55)
            .margin({ top: 17 })
            .onClick(() => {
                if (index === this.selectedIndex) {
                return
              }
              this.selectedIndex = index
              this.onSelectedIndexChange(this.selectedIndex)
            })
          }
        }, item => item.deviceName)
      }
    }
  }
}

 

(3) 遠端設備拉起

 

通過startAbility(deviceId)方法拉起遠端設備的FA,代碼如下:

 

startAbility(deviceId) {
  featureAbility.startAbility({
    want: {
      bundleName: 'ohos.samples.DistributeCalc',
      abilityName: 'ohos.samples.DistributeCalc.MainAbility',
      deviceId: deviceId,
      parameters: {
        isFA: 'FA'
      }
    }
  }).then((data) => {
    this.startAbilityCallBack(DATA_CHANGE)
  })
}


3. 分散式數據管理

 

分散式數據管理用於實現協同計算時數據在多端設備之間的相互同步。我們需要創建一個分散式資料庫來保存協同計算時數據,並通過分散式數據通信進行同步。


(1) 管理分散式資料庫

 

創建一個KVManager對象實例,用於管理分散式資料庫對象。代碼如下:

 

async createKvStore(callback) {
  //創建一個KVManager對象實例 
  this.kvManager = await distributedData.createKVManager(config)
  let options = {
    createIfMissing: true,
    encrypt: false,
    backup: false,
    autoSync: true,
    kvStoreType: 1,
    securityLevel: 1,
  }
  // 通過指定Options和storeId,創建並獲取KVStore資料庫,並通過Promise方式返回,此方法為非同步方法。
  this.kvStore = await this.kvManager.getKVStore(STORE_ID, options)
  callback()
}


(2) 訂閱分散式數據變化

 

通過訂閱分散式資料庫所有(本地及遠端)數據變化實現數據協同,代碼如下:

 

kvStoreModel.setOnMessageReceivedListener(DATA_CHANGE, (value) => {
  if (this.isDistributed) {
    if (value.search(EXIT) != -1) {
      Logger.info(TAG, `EXIT ${EXIT}`)
      featureAbility.terminateSelf((error) => {
        Logger.error(TAG, `terminateSelf finished, error= ${error}`)
      });
    } else {
      this.expression = value
      if (this.isOperator(this.expression.substr(this.expression.length - 1, this.expression.length))) {
        this.result = JSON.stringify(MATH.evaluate(this.expression.substr(0, this.expression.length - 1)))
      } else {
        this.result = JSON.stringify(MATH.evaluate(this.expression))
      }
    }
  }
})


至此,具有分散式能力的計算器就實現了。期待廣大開發者能基於TS擴展的聲明式開發範式開發出更多有趣的應用。

 

點擊鏈接,可獲取分散式計算器完整代碼:https://gitee.com/openharmony/app_samples/tree/master/Preset/DistributeCalc

 

 

搜索

複製


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • chmod怎麼用,Linux文件許可權管理 本文翻譯自Linux官網的Linux入門文章《File Permissions - chmod》,其中一些部分自作主張做了些修改 原文鏈接:File Permissions - chmod 原文 導言 Linux從UNIX繼承了文件所有權和許可權的觀念。這是因 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 1. 問題描述 電腦上成功安裝VMware虛擬機後,安裝Ubuntu系統。Ubuntu系統無法聯網,多方檢查發現問題:宿主機的網路連接中沒有VMware Network Adapter VMnet1和VMware Network Adapter ...
  • 已有的docker容器增加新的埠映射 在運行容器時指定映射埠運行後,如果想要添加新的埠映射,使用兩種方式都可以,需要的朋友可以參考下 背景 一般在運行容器時,我們都會通過參數 -p(使用大寫的-P參數則會隨機選擇宿主機的一個埠進行映射)來指定宿主機和容器埠的映射,例如 docker run ...
  • 本文例子參考《STM32單片機開發實例——基於Proteus虛擬模擬與HAL/LL庫》 源代碼:https://github.com/LanLinnet/STM33F103R6 寫在前面 在前面幾節的基礎上,我們已經基本瞭解了STM32F103的GPIO、外部中斷、定時器、串口通信和一些片內外設,接 ...
  • ethtool ethtool的使用 不帶選項,預設輸出協商速率、最大速率、連接狀態等信息 -i | --driver 列印驅動信息 --set-priv-flags 設置網卡的私有屬性,比如將link-down-on-close置為true後可以使用ifconfig down去關閉網卡連接 -a ...
  • 一、概述 DataX 是阿裡雲 DataWorks數據集成 的開源版本,在阿裡巴巴集團內被廣泛使用的離線數據同步工具/平臺。DataX 實現了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、HDFS、Hive、ADS、HBase、TableStore(OTS) ...
  • 1、查詢用戶所擁有的安全形色: select su.FullName,r.Name,bu.name bussinessname from SystemUserRoles sur left join SystemUserBase su on su.SystemUserId = sur.SystemUs ...
  • **導讀:**數據安全立法2018年9月於十三屆全國人大常委會列入立法規劃。經過三次審議,在2021年6月10日,十三屆全國人大常委會第二十九次會議正式表決通過,並於2021年9月1日起施行。從法律角度來說,國家對於數據安全越來越重視,作為企業該如何針對數據安全法進行數據安全治理的規劃,最終進行對應 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...