HarmonyOS ArkUI 3.0試玩初體驗!

「來源: |51CTO技術棧 ID:blog51cto」

HarmonyOS ArkUI 3。0 正式到來,今天就給大家分享一下我的 HarmonyOS ArkUI 3。0 框架試玩初體驗,以合成 1024 的開發實戰,帶大家感受一下 HarmonyOS ArkUI 3。0 的極簡開發。

HarmonyOS ArkUI 3.0試玩初體驗!

效果圖如下:

HarmonyOS ArkUI 3.0試玩初體驗!

程式碼檔案結構:

HarmonyOS ArkUI 3.0試玩初體驗!

建立一個空白的工程

①安裝和配置 DevEco Studio

DevEco Studio 下載:

https://developer。harmonyos。com/cn/develop/deveco-studio#download

DevEco Studio 安裝:

https://developer。harmonyos。com/cn/docs/documentation/doc-guides/software_install-0000001053582415

②建立一個 Empty Ability 應用

DevEco Studio 下載安裝成功後,開啟 DevEco Studio,點選左上角的 File,點選 New,再選擇 New Project,選擇 Empty Ability 選項,點選 Next 按鈕。

HarmonyOS ArkUI 3.0試玩初體驗!

將檔案命名為 MyETSApplication(檔名不能出現中文或者特殊字元,否則將無法成功建立專案檔案),Project Type 勾選 Application,選擇儲存路徑。

Language 勾選 eTS,選擇 API7,裝置勾選 Phone,最後點選 Finish 按鈕。

HarmonyOS ArkUI 3.0試玩初體驗!

③準備工作

在 entry>src>main>config。json 檔案中最下方“launchType”: “standard”的後面新增以下程式碼,這樣就可以實現去掉應用上方的標籤欄了。

config。json 最下方部分程式碼:

“metaData”: {

“customizeData”: [

{

“name”: “hwc-theme”,

“value”: “androidhwext:style/Theme。Emui。Light。NoTitleBar”,

“extra”: “”

}

}

實現介面佈局

①儲存圖片

將 logo 圖片儲存到 entry>src>main>resources>base>media 檔案中。

HarmonyOS ArkUI 3.0試玩初體驗!

②新一代的宣告式 UI 開發正規化

具體而言,ArkUI 3。0 中的新一代宣告式 UI 開發正規化,主要特徵如下:

(1)基於 TypeScript 擴充套件的宣告式 UI 描述語法,提供了類自然語言的UI描述和組合。

(2)開箱即用的多型元件。多型是指 UI 描述是統一的,UI 呈現在不同型別裝置上會有所不同。比如 Button 元件在手機和手錶會有不同的樣式和互動方式。

(3)多維度的狀態管理機制,支援靈活的資料驅動的 UI 變更。

裝飾器:用來裝飾類、結構體、方法以及變數,賦予其特殊的含義,如上述示例中 @Entry、@Component、@State 都是裝飾器。

@Component 表示這是個自定義元件;@Entry 則表示這是個入口元件;@State 表示元件中的狀態變數,這個狀態變化會引起 UI 變更。

自定義元件:可複用的 UI 單元,可組合其它元件,如上述被 @Component 裝飾的 struct Hello。

UI 描述:宣告式的方式來描述UI的結構,如上述 build() 方法內部的程式碼塊。

內建元件:框架中預設內建的基礎和佈局元件,可直接被開發者呼叫,比如示例中的 Column、Text、Divider、Button。

事件方法:用於新增元件對事件的響應邏輯,統一透過事件方法進行設定,如跟隨在 Button 後面的 on​Click()。

屬性方法:用於元件屬性的配置,統一透過屬性方法進行設定,如 fontSize()、width()、height()、color() 等,可透過鏈式呼叫的方式設定多項屬性。

③實現介面

這一次程式用到的裝飾器分別有 @Entry 、@Component、@State和 @Link 。

裝飾器 @Entry 裝飾的自定義元件用作頁面的預設入口元件,載入頁面時,將首先建立並呈現 @Entry 裝飾的自定義元件。

要注意的是:在單個原始檔中,最多可以使用 @Entry 裝飾一個自定義元件。

裝飾器 @Component 裝飾的 struct 表示該結構體具有元件化能力,能夠成為一個獨立的元件,這種型別的元件也稱為自定義元件。該元件可以組合其他元件,它透過實現 build 方法來描述 UI 結構。

元件生命週期包括:

aboutToAppear:

函式在建立自定義元件的新例項後,在執行其build函式之前執行。允許在 aboutToAppear 函式中改變狀態變數,這些更改將在後續執行build函式中生效。

aboutToDisappear:

函式在自定義元件析構消耗之前執行。不允許在 aboutToDisappear 函式中改變狀態變數,特別是 @Link 變數的修改可能會導致應用程式行為不穩定。

onPageShow:

當此頁面顯示時觸發一次。包括路由過程、應用進入前後臺等場景,僅 @Entry 修飾的自定義元件生效。

onPageHide:

當此頁面消失時觸發一次。包括路由過程、應用進入前後臺等場景,僅 @Entry 修飾的自定義元件生效。

onBackPress:

當用戶點選返回按鈕時觸發,,僅 @Entry 修飾的自定義元件生效。

裝飾器 @State 裝飾的變數是元件內部的狀態資料,當這些狀態資料被修改時,將會呼叫所在元件的 build 方法進行 UI 重新整理。

要注意的是,標記為 @State 的屬性不能直接在元件外部修改,必須為所有 @State 變數分配初始值。

裝飾器 @Link 裝飾的變數可以和父元件的 @State 變數建立雙向資料繫結。要注意的是,@Link 變數不能在元件內部進行初始化,在建立元件的新例項時,必須使用命名引數初始化所有 @Link 變數。

@Link 變數可以使用 @State 變數或 @Link 變數的引用進行初始化。@State 變數可以透過’$‘運算子建立引用。

index.ets:

先定義一個方格的背景顏色字典 colors,用以繪製不同數字的背景顏色,和一個全域性變數 number,用以 ForEach 的鍵值生成。

var number = 1

const colors={

“0”: “#CDC1B4”,

“2”: “#EEE4DA”,

“4”: “#ECE0C6”,

“8”: “#F2B179”,

“16”: “#F59563”,

“32”: “#F67C5F”,

“64”: “#F65E3B”,

“128”: “#EDCF72”,

“256”: “#EDCC61”,

“512”: “#99CC00”,

“1024”: “#83AF9B”,

“2048”: “#0099CC”,

“4096”: “#0099CC”,

“8192”: “#0099CC”

}

對於 4x4 的方格,如果要一個一個繪製,那麼就需要重複繪製 16 個 Text 元件,而且這些 Text 元件除了文字之外,其他屬性值都是一樣的,這樣極其繁瑣且沒有必要,體現不了 HarmonyOS ArkUI 3。0 的極簡開發。

我們可以把 4x4 的方格以每一行定義成一個元件,每一行每一行地繪製,這樣能夠極大的減少程式碼量。

對於每一行元件,傳統的方式是重複繪製 4 個 Text 元件,而且這些 Text 元件除了文字之外,其他屬性值都是一樣的,同樣極其繁瑣且沒有必要。

我們可以採用 ForEach 迴圈渲染來繪製:

第一個引數必須是陣列:允許空陣列,空陣列場景下不會建立子元件。同時允許設定返回值為陣列型別的函式。

例如 arr。slice(1, 3),設定的函式不得改變包括陣列本身在內的任何狀態變數,如 Array。splice、Array。sort 或 Array。reverse 這些原地修改陣列的函式。

第二個引數用於生成子元件的 lambda 函式。它為給定陣列項生成一個或多個子元件。單個元件和子元件列表必須括在大括號“{…}”中。

可選的第三個引數是用於鍵值生成的匿名函式。它為給定陣列項生成唯一且穩定的鍵值。

當子項在陣列中的位置更改時,子項的鍵值不得更改,當陣列中的子項被新項替換時,被替換項的鍵值和新項的鍵值必須不同。

鍵值生成器的功能是可選的。但是,出於效能原因,強烈建議提供,這使開發框架能夠更好地識別陣列更改。

如單擊進行陣列反向時,如果沒有提供鍵值生成器,則 ForEach 中的所有節點都將重建。

使用裝飾器 @Component,自定義一個每一行的元件,用裝飾器 @Link 定義一個數組 grids。

在 build() 裡面新增彈性佈局 Flex,使用迴圈渲染 ForEach 來繪製元件 Text。

對於每一個 Text 元件,文字判斷是否為 0,如果值為 0,則不顯示,背景顏色採用剛才定義好的背景顏色字典 colors 對應的背景顏色。

文字顏色判斷其值是否為 2 或 4,如果為 2 或 4,則採用顏色 #645B52,否則採用背景顏色白色。

@Component

struct setText {

@Link grids: number[]

build() {

Flex({ justifyContent: FlexAlign。Center, alignItems: ItemAlign。Center, direction: FlexDirection。Row }) {

ForEach(this。grids,

(item: number) => Text(item == 0 ? ’‘ : item。toString())

。width(70)

。height(70)

。textAlign(TextAlign。Center)

。fontSize(30)

。margin({ left: 5, top: 5, right: 5, bottom: 5 })

。backgroundColor(colors[item。toString()])

。fontColor((item == 2 || item == 4) ? ’#645B52‘ : ’#FFFFFF‘),

(item: number) => (number++) + item。toString())

}

}

}

同理,使用裝飾器 @Component,自定義一個按鈕 Button 元件,用以繪製上下左右四個按鈕。

@Component

struct setButton {

private dirtext: string

private dir: string

@Link Grids: number[][]

@Link grid1: number[]

@Link grid2: number[]

@Link grid3: number[]

@Link grid4: number[]

build() {

Button(this。dirtext)

。width(60)

。height(60)

。fontSize(30)

。fontWeight(FontWeight。Bold)

。align(Alignment。Center)

。backgroundColor(’#974B31‘)

。fontColor(’#FFFFFF‘)

。margin({ left: 5, top: 3, right: 5, bottom: 3 })

}

}

在裝飾器 @Entry 裝飾的結構體的 build() 中,將原來的程式碼全部刪掉。

使用裝飾器 @State 定義一個二維陣列和四個一維陣列,新增垂直佈局 Column,寬和高都為 100%,背景顏色為白色。

在其中新增 Image 元件,引用剛才儲存好的 logo 圖片,再新增一個寬和高都是 320,背景顏色為 #BBADA0 的垂直佈局 Column,在其新增四個剛才定義好的行元件 setText。

在外圍的垂直佈局 Column 中再新增四個剛才定義好的按鈕元件 setButton,其中中間兩個按鈕元件位於彈性佈局 Flex 中,最後新增一個 Button 元件,文字內容為“重新開始”。

@Entry

@Component

struct Index {

@State grids: number[][] = [[0, 0, 0, 0],

[0, 2, 0, 0],

[0, 0, 2, 0],

[0, 0, 0, 0]]

@State grid1: number[] = [this。grids[0][0], this。grids[0][1], this。grids[0][2], this。grids[0][3]]

@State grid2: number[] = [this。grids[1][0], this。grids[1][1], this。grids[1][2], this。grids[1][3]]

@State grid3: number[] = [this。grids[2][0], this。grids[2][1], this。grids[2][2], this。grids[2][3]]

@State grid4: number[] = [this。grids[3][0], this。grids[3][1], this。grids[3][2], this。grids[3][3]]

build() {

Column() {

Image($r(’app。media。logo1024‘))。width(’100%‘)。height(140)。align(Alignment。Center)

Column() {

setText({ grids: $grid1 })

setText({ grids: $grid2 })

setText({ grids: $grid3 })

setText({ grids: $grid4 })

}

。width(320)

。height(320)

。backgroundColor(“#BBADA0”)

setButton({dirtext: ’↑‘, dir: ’up‘, Grids: $grids, grid1: $grid1, grid2: $grid2, grid3: $grid3, grid4: $grid4})

Flex({ justifyContent: FlexAlign。Center, alignItems: ItemAlign。Center, direction: FlexDirection。Row }) {

setButton({dirtext: ’←‘, dir: ’left‘, Grids: $grids, grid1: $grid1, grid2: $grid2, grid3: $grid3, grid4: $grid4})

setButton({dirtext: ’→‘, dir: ’right‘, Grids: $grids, grid1: $grid1, grid2: $grid2, grid3: $grid3, grid4: $grid4})

}

setButton({dirtext: ’↓‘, dir: ’down‘, Grids: $grids, grid1: $grid1, grid2: $grid2, grid3: $grid3, grid4: $grid4})

Button(’重新開始‘)

。width(180)

。height(50)

。fontSize(30)

。align(Alignment。Center)

。backgroundColor(’#974B31‘)

。fontColor(’#FFFFFF‘)

。margin({ left: 5, top: 3, right: 5, bottom: 3 })

}

。width(’100%‘)

。height(’100%‘)

。backgroundColor(“#FFFFFF”)

。alignItems(HorizontalAlign。Center)

}

}

編寫邏輯程式碼

index。ets:在結構體 setButton 中新增四個函式。

addTwoOrFourToGrids():

用以隨機生成一個新的方格數字,數字為2或4。

swipeGrids(direction):

用以實現方格的重新生成。

changeGrids(direction):

用以實現方格的上下左右移動。

changeString():

用以將二維陣列分成四個一維陣列。

最後在 Button 元件的屬性裡新增一個點選事件,依次呼叫函式 swipeGrids(direction)、addTwoOrFourToGrids() 和 changeString()。

@Component

struct setButton {

private dirtext: string

private dir: string

@Link Grids: number[][]

@Link grid1: number[]

@Link grid2: number[]

@Link grid3: number[]

@Link grid4: number[]

addTwoOrFourToGrids() {

let array = [];

for (let row = 0; row < 4; row++)

for (let column = 0;column < 4; column++)

if (this。Grids[row][column] == 0)

array。push([row, column]);

let randomIndes = Math。floor(Math。random() * array。length);

let row = array[randomIndes][0];

let column = array[randomIndes][1];

if (Math。random() < 0。8) {

this。Grids[row][column] = 2;

} else {

this。Grids[row][column] = 4;

}

}

swipeGrids(direction) {

let newGrids = this。changeGrids(direction);

if (newGrids。toString() != this。Grids。toString()) {

this。Grids = newGrids;

}

}

changeGrids(direction) {

let newGrids = [[0, 0, 0, 0],

[0, 0, 0, 0],

[0, 0, 0, 0],

[0, 0, 0, 0]];

if (direction == ’left‘ || direction == ’right‘) {

let step = 1;

if (direction == ’right‘) {

step = -1;//step作為迴圈時陣列下標改變的方向

}

for (let row = 0; row < 4; row++) {//每一層

let array = [];

let column = 0;//如果為left則從0開始right從3開始,

if (direction == ’right‘) {

column = 3;

}

for (let i = 0; i < 4; i++) {

if (this。Grids[row][column] != 0) {//把所有非零元依次放入陣列中

array。push(this。Grids[row][column]);

}

column += step;//當direction為left時則從0向3遞增,為right時則從3向0遞減

}

for (let i = 0; i < array。length - 1; i++) {//訪問當前元素及他的下一個元素,所有迴圈次數為length-1

if (array[i] == array[i + 1]) {//判斷是否可合併,

array[i] += array[i + 1];//合併,

array[i + 1] = 0;//合併後參與合併的第二個元素消失

i++;

}

}

column = 0;

if (direction == ’right‘) {

column = 3;

}

for (const elem of array) {

if (elem != 0) {//跳過array裡的空元素

newGrids[row][column] = elem;//把合併後的狀態賦給新陣列grids,

column += step;

}

}

}

} elseif (direction == ’up‘ || direction == ’down‘) {//同理

let step = 1;

if (direction == ’down‘) {

step = -1;

}

for (let column = 0; column < 4; column++) {

let array = [];

let row = 0;

if (direction == ’down‘) {

row = 3;

}

for (let i = 0; i < 4; i++) {

if (this。Grids[row][column] != 0) {

array。push(this。Grids[row][column]);

}

row += step;

}

for (let i = 0; i < array。length - 1; i++) {

if (array[i] == array[i + 1]) {

array[i] += array[i + 1];

array[i + 1] = 0;

i++;

}

}

row = 0;

if (direction == ’down‘) {

row = 3;

}

for (const elem of array) {

if (elem != 0) {

newGrids[row][column] = elem;

row += step;

}

}

}

}

return newGrids;

}

changeString() {

this。grid1 = [this。Grids[0][0], this。Grids[0][1], this。Grids[0][2], this。Grids[0][3]]

this。grid2 = [this。Grids[1][0], this。Grids[1][1], this。Grids[1][2], this。Grids[1][3]]

this。grid3 = [this。Grids[2][0], this。Grids[2][1], this。Grids[2][2], this。Grids[2][3]]

this。grid4 = [this。Grids[3][0], this。Grids[3][1], this。Grids[3][2], this。Grids[3][3]]

}

build() {

Button(this。dirtext)

。width(60)

。height(60)

。fontSize(30)

。fontWeight(FontWeight。Bold)

。align(Alignment。Center)

。backgroundColor(’#974B31‘)

。fontColor(’#FFFFFF‘)

。margin({ left: 5, top: 3, right: 5, bottom: 3 })

。on​Click((event: ClickEvent) => {

this。swipeGrids(this。dir)

this。addTwoOrFourToGrids()

this。changeString()

})

}

}

在結構體 index 中文字內容為“重新開始”的按鈕新增一個點選事件,用以重新初始化資料。

@Entry

@Component

struct Index {

@State grids: number[][] = [[0, 0, 0, 0],

[0, 2, 0, 0],

[0, 0, 2, 0],

[0, 0, 0, 0]]

@State grid1: number[] = [this。grids[0][0], this。grids[0][1], this。grids[0][2], this。grids[0][3]]

@State grid2: number[] = [this。grids[1][0], this。grids[1][1], this。grids[1][2], this。grids[1][3]]

@State grid3: number[] = [this。grids[2][0], this。grids[2][1], this。grids[2][2], this。grids[2][3]]

@State grid4: number[] = [this。grids[3][0], this。grids[3][1], this。grids[3][2], this。grids[3][3]]

build() {

Column() {

Image($r(’app。media。logo1024‘))。width(’100%‘)。height(140)。align(Alignment。Center)

Column() {

setText({ grids: $grid1 })

setText({ grids: $grid2 })

setText({ grids: $grid3 })

setText({ grids: $grid4 })

}

。width(320)

。height(320)

。backgroundColor(“#BBADA0”)

setButton({dirtext: ’↑‘, dir: ’up‘, Grids: $grids, grid1: $grid1, grid2: $grid2, grid3: $grid3, grid4: $grid4})

Flex({ justifyContent: FlexAlign。Center, alignItems: ItemAlign。Center, direction: FlexDirection。Row }) {

setButton({dirtext: ’←‘, dir: ’left‘, Grids: $grids, grid1: $grid1, grid2: $grid2, grid3: $grid3, grid4: $grid4})

setButton({dirtext: ’→‘, dir: ’right‘, Grids: $grids, grid1: $grid1, grid2: $grid2, grid3: $grid3, grid4: $grid4})

}

setButton({dirtext: ’↓‘, dir: ’down‘, Grids: $grids, grid1: $grid1, grid2: $grid2, grid3: $grid3, grid4: $grid4})

Button(’重新開始‘)

。width(180)

。height(50)

。fontSize(30)

。align(Alignment。Center)

。backgroundColor(’#974B31‘)

。fontColor(’#FFFFFF‘)

。margin({ left: 5, top: 3, right: 5, bottom: 3 })

。on​Click((event: ClickEvent)=>{

this。grids = [[0, 0, 0, 0],

[0, 2, 0, 0],

[0, 0, 2, 0],

[0, 0, 0, 0]]

this。grid1 = [this。grids[0][0], this。grids[0][1], this。grids[0][2], this。grids[0][3]]

this。grid2 = [this。grids[1][0], this。grids[1][1], this。grids[1][2], this。grids[1][3]]

this。grid3 = [this。grids[2][0], this。grids[2][1], this。grids[2][2], this。grids[2][3]]

this。grid4 = [this。grids[3][0], this。grids[3][1], this。grids[3][2], this。grids[3][3]]

})

}

。width(’100%‘)

。height(’100%‘)

。backgroundColor(“#FFFFFF”)

。alignItems(HorizontalAlign。Center)

}

}

寫在最後

HarmonyOS ArkUI 3。0 框架還有很多內容在本次程式中沒有涉及到,例如頁面跳轉、資料管理、分散式資料庫、分散式流轉、分散式協同等等,我會在以後的文章中陸陸續續分享我的實戰操作,希望能與各位一起學習相互交流!

https://gitee。com/hiharmonica/awesome-harmony-os-kapok

瞭解

鴻蒙

一手資訊

求分享

求點贊

求在看