「來源: |51CTO技術棧 ID:blog51cto」
HarmonyOS ArkUI 3。0 正式到來,今天就給大家分享一下我的 HarmonyOS ArkUI 3。0 框架試玩初體驗,以合成 1024 的開發實戰,帶大家感受一下 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 按鈕。
將檔案命名為 MyETSApplication(檔名不能出現中文或者特殊字元,否則將無法成功建立專案檔案),Project Type 勾選 Application,選擇儲存路徑。
Language 勾選 eTS,選擇 API7,裝置勾選 Phone,最後點選 Finish 按鈕。
③準備工作
在 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 檔案中。
②新一代的宣告式 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 後面的 onClick()。
屬性方法:用於元件屬性的配置,統一透過屬性方法進行設定,如 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 })
。onClick((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 })
。onClick((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
瞭解
鴻蒙
一手資訊
求分享
求點贊
求在看