HarmonyOS ArkUI 3.0正式到來,今天就給大家分享一下我的 HarmonyOS ArkUI 3.0 框架試玩初體驗,以合成 1024 的開發(fā)實戰(zhàn),帶大家感受一下 HarmonyOS ArkUI 3.0 的極簡開發(fā)。
創(chuàng)建一個空白的工程
①安裝和配置 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
②創(chuàng)建一個 Empty Ability 應(yīng)用
DevEco Studio 下載安裝成功后,打開 DevEco Studio,點擊左上角的 File,點擊 New,再選擇 New Project,選擇 Empty Ability 選項,點擊 Next 按鈕。
將文件命名為 MyETSApplication(文件名不能出現(xiàn)中文或者特殊字符,否則將無法成功創(chuàng)建項目文件),Project Type 勾選 Application,選擇保存路徑。
Language 勾選 eTS,選擇 API7,設(shè)備勾選 Phone,最后點擊 Finish 按鈕。
③準(zhǔn)備工作
在 entry>src>main>config.json 文件中最下方"launchType": "standard"的后面添加以下代碼,這樣就可以實現(xiàn)去掉應(yīng)用上方的標(biāo)簽欄了。
config.json 最下方部分代碼:
"metaData":{
"customizeData":[
{
"name":"hwc-theme",
"value":"androidhwext:style/Theme.Emui.Light.NoTitleBar",
"extra":""
}
]
}
實現(xiàn)界面布局
①保存圖片
將 logo 圖片保存到 entry>src>main>resources>base>media 文件中。
②新一代的聲明式 UI 開發(fā)范式
具體而言,ArkUI 3.0 中的新一代聲明式 UI 開發(fā)范式,主要特征如下:(1)基于 TypeScript 擴(kuò)展的聲明式 UI 描述語法,提供了類自然語言的UI描述和組合。
(2)開箱即用的多態(tài)組件。多態(tài)是指 UI 描述是統(tǒng)一的,UI 呈現(xiàn)在不同類型設(shè)備上會有所不同。比如 Button 組件在手機(jī)和手表會有不同的樣式和交互方式。
(3)多維度的狀態(tài)管理機(jī)制,支持靈活的數(shù)據(jù)驅(qū)動的 UI 變更。
裝飾器:用來裝飾類、結(jié)構(gòu)體、方法以及變量,賦予其特殊的含義,如上述示例中 @Entry、@Component、@State 都是裝飾器。
@Component 表示這是個自定義組件;@Entry 則表示這是個入口組件;@State 表示組件中的狀態(tài)變量,這個狀態(tài)變化會引起 UI 變更。
自定義組件:可復(fù)用的 UI 單元,可組合其它組件,如上述被 @Component 裝飾的 struct Hello。
UI 描述:聲明式的方式來描述UI的結(jié)構(gòu),如上述 build() 方法內(nèi)部的代碼塊。
內(nèi)置組件:框架中默認(rèn)內(nèi)置的基礎(chǔ)和布局組件,可直接被開發(fā)者調(diào)用,比如示例中的 Column、Text、Divider、Button。
事件方法:用于添加組件對事件的響應(yīng)邏輯,統(tǒng)一通過事件方法進(jìn)行設(shè)置,如跟隨在 Button 后面的 onClick()。
屬性方法:用于組件屬性的配置,統(tǒng)一通過屬性方法進(jìn)行設(shè)置,如 fontSize()、width()、height()、color() 等,可通過鏈?zhǔn)秸{(diào)用的方式設(shè)置多項屬性。
③實現(xiàn)界面
這一次程序用到的裝飾器分別有@Entry 、@Component、@State和@Link 。
裝飾器@Entry 裝飾的自定義組件用作頁面的默認(rèn)入口組件,加載頁面時,將首先創(chuàng)建并呈現(xiàn) @Entry 裝飾的自定義組件。
要注意的是:在單個源文件中,最多可以使用 @Entry 裝飾一個自定義組件。
裝飾器@Component 裝飾的 struct 表示該結(jié)構(gòu)體具有組件化能力,能夠成為一個獨立的組件,這種類型的組件也稱為自定義組件。該組件可以組合其他組件,它通過實現(xiàn) build 方法來描述 UI 結(jié)構(gòu)。 組件生命周期包括:
-
aboutToAppear:函數(shù)在創(chuàng)建自定義組件的新實例后,在執(zhí)行其build函數(shù)之前執(zhí)行。允許在 aboutToAppear 函數(shù)中改變狀態(tài)變量,這些更改將在后續(xù)執(zhí)行build函數(shù)中生效。
-
aboutToDisappear:函數(shù)在自定義組件析構(gòu)消耗之前執(zhí)行。不允許在 aboutToDisappear 函數(shù)中改變狀態(tài)變量,特別是 @Link 變量的修改可能會導(dǎo)致應(yīng)用程序行為不穩(wěn)定。
-
onPageShow:當(dāng)此頁面顯示時觸發(fā)一次。包括路由過程、應(yīng)用進(jìn)入前后臺等場景,僅 @Entry 修飾的自定義組件生效。
-
onPageHide:當(dāng)此頁面消失時觸發(fā)一次。包括路由過程、應(yīng)用進(jìn)入前后臺等場景,僅 @Entry 修飾的自定義組件生效。
-
onBackPress:當(dāng)用戶點擊返回按鈕時觸發(fā),,僅 @Entry 修飾的自定義組件生效。
裝飾器@State 裝飾的變量是組件內(nèi)部的狀態(tài)數(shù)據(jù),當(dāng)這些狀態(tài)數(shù)據(jù)被修改時,將會調(diào)用所在組件的 build 方法進(jìn)行 UI 刷新。
要注意的是,標(biāo)記為 @State 的屬性不能直接在組件外部修改,必須為所有 @State 變量分配初始值。
裝飾器@Link 裝飾的變量可以和父組件的 @State 變量建立雙向數(shù)據(jù)綁定。要注意的是,@Link 變量不能在組件內(nèi)部進(jìn)行初始化,在創(chuàng)建組件的新實例時,必須使用命名參數(shù)初始化所有 @Link 變量。
@Link 變量可以使用 @State 變量或 @Link 變量的引用進(jìn)行初始化。@State 變量可以通過’$'操作符創(chuàng)建引用。
index.ets:先定義一個方格的背景顏色字典 colors,用以繪制不同數(shù)字的背景顏色,和一個全局變量 number,用以 ForEach 的鍵值生成。
varnumber=1
constcolors={
"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 的方格,如果要一個一個繪制,那么就需要重復(fù)繪制 16 個 Text 組件,而且這些 Text 組件除了文本之外,其他屬性值都是一樣的,這樣極其繁瑣且沒有必要,體現(xiàn)不了 HarmonyOS ArkUI 3.0 的極簡開發(fā)。
我們可以把 4x4 的方格以每一行定義成一個組件,每一行每一行地繪制,這樣能夠極大的減少代碼量。
對于每一行組件,傳統(tǒng)的方式是重復(fù)繪制 4 個 Text 組件,而且這些 Text 組件除了文本之外,其他屬性值都是一樣的,同樣極其繁瑣且沒有必要。
我們可以采用 ForEach 循環(huán)渲染來繪制:
第一個參數(shù)必須是數(shù)組:允許空數(shù)組,空數(shù)組場景下不會創(chuàng)建子組件。同時允許設(shè)置返回值為數(shù)組類型的函數(shù)。
例如 arr.slice(1, 3),設(shè)置的函數(shù)不得改變包括數(shù)組本身在內(nèi)的任何狀態(tài)變量,如 Array.splice、Array.sort 或 Array.reverse 這些原地修改數(shù)組的函數(shù)。
第二個參數(shù)用于生成子組件的 lambda 函數(shù)。它為給定數(shù)組項生成一個或多個子組件。單個組件和子組件列表必須括在大括號“{…}”中。
可選的第三個參數(shù)是用于鍵值生成的匿名函數(shù)。它為給定數(shù)組項生成唯一且穩(wěn)定的鍵值。
當(dāng)子項在數(shù)組中的位置更改時,子項的鍵值不得更改,當(dāng)數(shù)組中的子項被新項替換時,被替換項的鍵值和新項的鍵值必須不同。
鍵值生成器的功能是可選的。但是,出于性能原因,強(qiáng)烈建議提供,這使開發(fā)框架能夠更好地識別數(shù)組更改。
如單擊進(jìn)行數(shù)組反向時,如果沒有提供鍵值生成器,則 ForEach 中的所有節(jié)點都將重建。
使用裝飾器 @Component,自定義一個每一行的組件,用裝飾器 @Link 定義一個數(shù)組 grids。
在 build() 里面添加彈性布局 Flex,使用循環(huán)渲染 ForEach 來繪制組件 Text。
對于每一個 Text 組件,文本判斷是否為 0,如果值為 0,則不顯示,背景顏色采用剛才定義好的背景顏色字典 colors 對應(yīng)的背景顏色。
文本顏色判斷其值是否為 2 或 4,如果為 2 或 4,則采用顏色 #645B52,否則采用背景顏色白色。
@Component
structsetText{
@Linkgrids: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
structsetButton{
privatedirtext:string
privatedir:string
@LinkGrids:number[][]
@Linkgrid1:number[]
@Linkgrid2:number[]
@Linkgrid3:number[]
@Linkgrid4: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 裝飾的結(jié)構(gòu)體的 build() 中,將原來的代碼全部刪掉。
使用裝飾器 @State 定義一個二維數(shù)組和四個一維數(shù)組,添加垂直布局 Column,寬和高都為 100%,背景顏色為白色。
在其中添加 Image 組件,引用剛才保存好的 logo 圖片,再添加一個寬和高都是 320,背景顏色為 #BBADA0 的垂直布局 Column,在其添加四個剛才定義好的行組件 setText。
在外圍的垂直布局 Column 中再添加四個剛才定義好的按鈕組件 setButton,其中中間兩個按鈕組件位于彈性布局 Flex 中,最后添加一個 Button 組件,文本內(nèi)容為“重新開始”。
@Entry
@Component
structIndex{
@Stategrids:number[][]=[[0,0,0,0],
[0,2,0,0],
[0,0,2,0],
[0,0,0,0]]
@Stategrid1:number[]=[this.grids[0][0],this.grids[0][1],this.grids[0][2],this.grids[0][3]]
@Stategrid2:number[]=[this.grids[1][0],this.grids[1][1],this.grids[1][2],this.grids[1][3]]
@Stategrid3:number[]=[this.grids[2][0],this.grids[2][1],this.grids[2][2],this.grids[2][3]]
@Stategrid4: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:在結(jié)構(gòu)體 setButton 中添加四個函數(shù)。-
addTwoOrFourToGrids():用以隨機(jī)生成一個新的方格數(shù)字,數(shù)字為2或4。
-
swipeGrids(direction):用以實現(xiàn)方格的重新生成。
-
changeGrids(direction):用以實現(xiàn)方格的上下左右移動。
-
changeString():用以將二維數(shù)組分成四個一維數(shù)組。
最后在 Button 組件的屬性里添加一個點擊事件,依次調(diào)用函數(shù) swipeGrids(direction)、addTwoOrFourToGrids() 和 changeString()。
@Component
structsetButton{
privatedirtext:string
privatedir:string
@LinkGrids:number[][]
@Linkgrid1:number[]
@Linkgrid2:number[]
@Linkgrid3:number[]
@Linkgrid4:number[]
addTwoOrFourToGrids(){
letarray=[];
for(letrow=0;row4;row++)
for(letcolumn=0;column4;column++)
if(this.Grids[row][column]==0)
array.push([row,column]);
letrandomIndes=Math.floor(Math.random()*array.length);
letrow=array[randomIndes][0];
letcolumn=array[randomIndes][1];
if(Math.random()0.8){
this.Grids[row][column]=2;
}else{
this.Grids[row][column]=4;
}
}
swipeGrids(direction){
letnewGrids=this.changeGrids(direction);
if(newGrids.toString()!=this.Grids.toString()){
this.Grids=newGrids;
}
}
changeGrids(direction){
letnewGrids=[[0,0,0,0],
[0,0,0,0],
[0,0,0,0],
[0,0,0,0]];
if(direction=='left'||direction=='right'){
letstep=1;
if(direction=='right'){
step=-1;//step作為循環(huán)時數(shù)組下標(biāo)改變的方向
}
for(letrow=0;row4;row++){//每一層
letarray=[];
letcolumn=0;//如果為left則從0開始right從3開始,
if(direction=='right'){
column=3;
}
for(leti=0;i4;i++){
if(this.Grids[row][column]!=0){//把所有非零元依次放入數(shù)組中
array.push(this.Grids[row][column]);
}
column+=step;//當(dāng)direction為left時則從0向3遞增,為right時則從3向0遞減
}
for(leti=0;i1;i++){//訪問當(dāng)前元素及他的下一個元素,所有循環(huán)次數(shù)為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(constelemofarray){
if(elem!=0){//跳過array里的空元素
newGrids[row][column]=elem;//把合并后的狀態(tài)賦給新數(shù)組grids,
column+=step;
}
}
}
}elseif(direction=='up'||direction=='down'){//同理
letstep=1;
if(direction=='down'){
step=-1;
}
for(letcolumn=0;column4;column++){
letarray=[];
letrow=0;
if(direction=='down'){
row=3;
}
for(leti=0;i4;i++){
if(this.Grids[row][column]!=0){
array.push(this.Grids[row][column]);
}
row+=step;
}
for(leti=0;i1;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(constelemofarray){
if(elem!=0){
newGrids[row][column]=elem;
row+=step;
}
}
}
}
returnnewGrids;
}
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()
})
}
}
在結(jié)構(gòu)體 index 中文本內(nèi)容為“重新開始”的按鈕添加一個點擊事件,用以重新初始化數(shù)據(jù)。
@Entry
@Component
structIndex{
@Stategrids:number[][]=[[0,0,0,0],
[0,2,0,0],
[0,0,2,0],
[0,0,0,0]]
@Stategrid1:number[]=[this.grids[0][0],this.grids[0][1],this.grids[0][2],this.grids[0][3]]
@Stategrid2:number[]=[this.grids[1][0],this.grids[1][1],this.grids[1][2],this.grids[1][3]]
@Stategrid3:number[]=[this.grids[2][0],this.grids[2][1],this.grids[2][2],this.grids[2][3]]
@Stategrid4: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 框架還有很多內(nèi)容在本次程序中沒有涉及到,例如頁面跳轉(zhuǎn)、數(shù)據(jù)管理、分布式數(shù)據(jù)庫、分布式流轉(zhuǎn)、分布式協(xié)同等等,我會在以后的文章中陸陸續(xù)續(xù)分享我的實戰(zhàn)操作,希望能與各位一起學(xué)習(xí)相互交流!
-
操作系統(tǒng)
+關(guān)注
關(guān)注
37文章
6859瀏覽量
123498 -
鴻蒙系統(tǒng)
+關(guān)注
關(guān)注
183文章
2637瀏覽量
66505 -
HarmonyOS
+關(guān)注
關(guān)注
79文章
1980瀏覽量
30326
原文標(biāo)題:HarmonyOS ArkUI 3.0試玩初體驗!
文章出處:【微信號:gh_834c4b3d87fe,微信公眾號:OpenHarmony技術(shù)社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論