第三篇 - ArkUI布局基礎(chǔ)與制作可交互頁(yè)面
導(dǎo)讀:在本篇文章里,您將掌握事件、裝飾器、雙向綁定等相關(guān)知識(shí),并利用所學(xué)知識(shí)做一個(gè)待辦列表的案例。
練手案例:登錄界面
開(kāi)始之前,先說(shuō)些題外話(huà)
貓林老師發(fā)現(xiàn)不少同學(xué)可以獨(dú)立寫(xiě)出來(lái),我很欣慰。說(shuō)明行動(dòng)力、悟性、之前前端留下的布局思想都還在。希望各位同學(xué)和更多的朋友們都能參與進(jìn)來(lái)。大家以后寫(xiě)完可以把自己的代碼或者效果貼到評(píng)論區(qū)相互討論。討論的人越多越有學(xué)習(xí)氛圍,在這里大家都可以找到志同道合的人。并且,每一次熱烈的討論都能激勵(lì)貓林老師更認(rèn)真迅速地去寫(xiě)下一篇文章。
而且寫(xiě)教程文真的很需要花費(fèi)額外時(shí)間,那就會(huì)擠占貓林老師想做愛(ài)做的事的時(shí)間??。而且純愛(ài)心發(fā)電其實(shí)動(dòng)力并不足,所以也真的很需要各位讀者提供熱烈的正向回饋來(lái)激勵(lì)貓林老師,所以大家請(qǐng)一定要多點(diǎn)贊、收藏、評(píng)論。要是可以,也分享給你周?chē)雽W(xué)鴻蒙的朋友。貓林老師保證把系列文章更新下去,讓大家從文章里就能學(xué)到真東西,并且具備找工作能力。
-
好了,話(huà)不多說(shuō),開(kāi)始說(shuō)回上次的作業(yè)案例,讓我們先回顧一下作業(yè)的效果圖: image.png
從上圖分析可以發(fā)現(xiàn)整體上所有內(nèi)容是從上往下布局,所以用Column作為根容器非常合適。
-
然后里面可以分為8行元素,分別為:Image、Text、Text、TextInput、TextInput、Row、Button、Text,如圖
image.png
這些都是比較容易看出來(lái)的布局,主要是給大家解釋下
短信驗(yàn)證碼登錄和忘記密碼那一行,為什么還要用一個(gè)Row包起來(lái)呢?因?yàn)槿绻@兩個(gè)文字不被Row包起來(lái)的話(huà),那么父組件是Column,那短信驗(yàn)證碼登錄和忘記密碼就會(huì)變成一行一個(gè)。所以用一個(gè)Row包起來(lái),因?yàn)镽ow有從左到右布局子組件的能力,而這兩個(gè)文字就需要從左到右,只不過(guò)一個(gè)在起點(diǎn),一個(gè)在終點(diǎn)(即在首尾),所以這里到時(shí)候還可以給它做一個(gè)主軸上的布局為SpaceBetween。其他的無(wú)非記得要讓根容器
Column鋪滿(mǎn)屏幕,也即寬高百分百,圖片給寬度、登錄界面給文字大小和加粗,登錄帳號(hào)以使用更多服務(wù)改文字顏色、文字大小。兩個(gè)TextInput給占位符,其中第二個(gè)TextInput記得要把type設(shè)置為password。其他剩余的三個(gè)label都是改文字顏色、字體大小。登錄按鈕給寬度鋪滿(mǎn)。然后整個(gè)頁(yè)面是灰色,所以給Column設(shè)置背景色,再給TextInput設(shè)置背景色為白色。-
根據(jù)上述分析,代碼如下
Column() { Image($r('app.media.app_icon')) .width(100) Text('登錄界面') .fontSize(24) .fontWeight(700) Text('登錄賬號(hào)以使用更多服務(wù)') .fontColor(Color.Gray) .fontSize(14) TextInput({ placeholder: '賬號(hào)' }) .backgroundColor('#fefefe') TextInput({ placeholder: '密碼' }) .backgroundColor('#fefefe') .type(InputType.Password) Row() { Text('短信驗(yàn)證碼登錄') .fontColor('#3172f3') Text('忘記密碼') .fontColor('#3172f3') } .justifyContent(FlexAlign.SpaceBetween) Button('登錄') .width('100%') Text('注冊(cè)賬號(hào)') .fontColor('#3172f3') } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) .backgroundColor('#eff1f3') 對(duì)應(yīng)效果如下:

-
此時(shí)大家發(fā)現(xiàn)兩個(gè)問(wèn)題:
- 明明
短信驗(yàn)證碼那一行的Row給了justifyContent(FlexAlign.SpaceBetween),但沒(méi)生效,看著還是居中。為什么呢? - 所有內(nèi)容行與行之間沒(méi)有間距,導(dǎo)致挨的太緊。
- 明明
-
這兩個(gè)問(wèn)題都很好很解決,分別如下
-
給了
FlexAlign.SpaceBetween也沒(méi)生效,是因?yàn)樨埩掷蠋熒瞎?jié)課就說(shuō)過(guò)大部分組件不給寬高就是靠?jī)?nèi)容撐開(kāi)寬高,也即內(nèi)容有大,Row就只有多大。所以你設(shè)置首尾對(duì)齊實(shí)際上它已經(jīng)是首尾了,只不過(guò)因?yàn)镽ow就那么大,所以效果不動(dòng)
image.png
所以解決辦法很簡(jiǎn)單:給Row一個(gè)width('100%')即可 行與行之間要設(shè)置間距可以給Column加
space
-
-
因此,改良代碼如下(僅寫(xiě)出本次改動(dòng)部分)
Column({ space: 10 }) { ...... Row() { Text('短信驗(yàn)證碼登錄') .fontColor('#3172f3') Text('忘記密碼') .fontColor('#3172f3') } .width('100%') .justifyContent(FlexAlign.SpaceBetween) ........ } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) .backgroundColor('#eff1f3') -
此時(shí)效果如下
image.png
-
發(fā)現(xiàn)間距是有了,但是中間的賬號(hào)、密碼、短信驗(yàn)證碼登錄這歌區(qū)域跟上面和下面太近,需要把這部分跟上下加一些間距
image.png
-
如上圖所示,發(fā)現(xiàn)這個(gè)時(shí)候我們應(yīng)該把中間這一部分作為一個(gè)整體,再統(tǒng)一設(shè)置整體的上下外間距即可。因此需要給中間的2個(gè)
TextInput和它下面包住兩個(gè)文字的Row再套一個(gè)父容器。這里請(qǐng)大家思考下,他們的父容器,用Column好還是Row好呢?- 沒(méi)錯(cuò),用
Column好!因?yàn)橛?code>Row他們會(huì)從左往右排列,而我們依然要它從上到下,只不過(guò)多個(gè)父容器而已,所以用Column
- 沒(méi)錯(cuò),用
-
那分析完加父容器后,還有個(gè)問(wèn)題。怎么設(shè)置這個(gè)父容器的距離外部的間距呢?會(huì)
css的同學(xué)知道,是margin,沒(méi)錯(cuò),ArkTS里也是margin(如果不懂什么叫margin的請(qǐng)?zhí)魬?zhàn)到本文最后的附錄:外間距與內(nèi)間距,再回此繼續(xù)觀(guān)看),語(yǔ)法如下組件() { } .margin(間距數(shù)) // 例 Column() { } .margin(20) // 代表這個(gè) Column 具體上下左右間距都是20 -
如果你不需要上下左右間距都是同一個(gè)值,則可以傳入一個(gè)對(duì)象,分別設(shè)置不同的margin,例
Column() { } .margin({ left: 20, right: 10, top: 15, bottom: 30 } ) // 代表這個(gè)Column左間距20,右間距10,上間距15,下間距30 -
也可以?xún)H設(shè)置幾個(gè)方向的間距,沒(méi)設(shè)置方向的間距代表為0,例
Column() { } .margin({ top: 10, bottom: 20 } ) // 代表這個(gè)Column上間距10,下間距20,左右間距因?yàn)闆](méi)設(shè)置,那么則代表0也即沒(méi)有間距 -
因此,案例代碼里先給中間部分加
Column,且設(shè)置外間距。并且因?yàn)橹虚g部分包了Colum后,他們各自之間也沒(méi)間距了,因此給包住驗(yàn)證碼登錄文字的Row再加一個(gè)上間距,TextInput不用加,因?yàn)樗麄儽旧砭鸵ぴ谝黄?,改?dòng)代碼如下Column({ space: 10 }) { ......... Column() { TextInput({ placeholder: '賬號(hào)' }) .backgroundColor('#fefefe') TextInput({ placeholder: '密碼' }) .backgroundColor('#fefefe') .type(InputType.Password) Row() { Text('短信驗(yàn)證碼登錄') .fontColor('#3172f3') Text('忘記密碼') .fontColor('#3172f3') } .margin({ top: 10 }) } .margin({ top: 20, bottom: 20 }) ....... } -
此時(shí)效果如下
image.png
-
沒(méi)錯(cuò),現(xiàn)在跟最終效果圖已經(jīng)差不多了,但是發(fā)現(xiàn)左右兩邊都挨到邊邊了,而效果圖需要左右兩邊都有點(diǎn)間距。這時(shí)候有兩種解決辦法:
- 給兩個(gè)TextInput、Row、Button這四行設(shè)置左右外間距
- 給他們共同的父組件設(shè)置內(nèi)間距
-
很明顯,用第二種給共同的父組件設(shè)置內(nèi)間距辦法更方便。但是ArkTS里如何設(shè)置呢?其實(shí)還是用
padding,并且用法跟margin是一樣的,例Column() { } .padding(20) // 上下左右四個(gè)方向內(nèi)間距都是20 Column() { } .padding({ top: 20, bottom: 10, left: 20, right: 25 }) // 上內(nèi)間距20,下內(nèi)間距10,左內(nèi)間距20,右內(nèi)間距25 Column() { } .padding({ left: 20, right: 20 }) // 左內(nèi)間距和右內(nèi)間距都是20,上下沒(méi)寫(xiě)則默認(rèn)是0 -
因此,再給根組件
Column設(shè)置padding,且只需要左右間距即可Column({ space: 10 }) { ........ } ....... .padding({ left: 10, right: 10 }) 至此,一個(gè)完整的
登錄界面就寫(xiě)完了,你學(xué)廢了嗎?

- 本案例新知識(shí):
- margin: 外間距
- padding :內(nèi)間距
事件
上面的登錄案例中,我們目前點(diǎn)
登錄按鈕是沒(méi)有任何反應(yīng)的。要想讓它有反應(yīng),必須添加事件,在A(yíng)rkTS中如何添加事件呢?-
語(yǔ)法
組件() { } .on事件名( e => { // 事件處理代碼 } ) -
例
Button('登錄') .onClick(e => { // 處理代碼 }) 注意:上面的Click里的C大寫(xiě)。后面的事件名如無(wú)特殊情況,都是要首字母大寫(xiě),例如change事件,寫(xiě)的時(shí)候要加
onChange,這跟前端里的全小寫(xiě)不一樣,大家要注意。-
e則是事件對(duì)象,但用的略少,不需要時(shí),可以不寫(xiě)e,替換為小括號(hào)
Button('登錄') .onClick(() => { // 處理代碼 })
提示框
如果現(xiàn)在,我希望點(diǎn)擊按鈕后彈出
登錄成功的提示框怎么辦呢?ArkUI里提供了PromptAction對(duì)象,專(zhuān)門(mén)用來(lái)做彈窗-
用法:
- 先導(dǎo)入,再調(diào)用
PromptAction對(duì)象的showToast方法,傳入對(duì)象,配置提示信息,例
import { promptAction } from '@kit.ArkUI' promptAction.showToast({ message: '提示消息', // 提示的文字 duration: 2000 // 顯示時(shí)長(zhǎng),不填則默認(rèn)為1500 }); - 先導(dǎo)入,再調(diào)用
技巧:可以在寫(xiě)代碼時(shí),直接寫(xiě)promptAction,然后出提示后按回車(chē),DevEco會(huì)自動(dòng)幫你生成導(dǎo)入的代碼,如下圖
image.png
- 注意:
- duration為提示框多久后消失(也即顯示時(shí)長(zhǎng)),可以不填,不填則默認(rèn)為1500,并且最小值也是1500,最大值是10000。如果填寫(xiě)的數(shù)字小于1500,也按1500來(lái)顯示,如果大于10000,也按10000來(lái)顯示。
- 單位是毫秒,1500即1.5秒
-
例:
promptAction.showToast({ message: '貓林老師教程真好!' })- 效果如下

- 更多彈窗的用法可閱讀官方文檔:點(diǎn)我跳轉(zhuǎn)至官方文檔
聲明組件內(nèi)成員變量
很多時(shí)候我們這個(gè)頁(yè)面(組件)需要聲明一些變量用來(lái)保存數(shù)據(jù),和對(duì)其處理。那么怎么聲明呢?
-
一般會(huì)寫(xiě)在
build函數(shù)的上面,struct關(guān)鍵子下面,即下圖位置
image.png
-
語(yǔ)法為:
變量名: 類(lèi)型 = 初始值 -
例:
userId: string = '' // 聲明了一個(gè)名為 userId 的變量,它是字符串類(lèi)型,初始值為空字符串 -
變量聲明好了,如何在代碼中使用呢?一律前面加this訪(fǎng)問(wèn),例
this.userId
雙向綁定
-
學(xué)會(huì)聲明成員變量后,我們?cè)?code>登錄案例 里,聲明兩個(gè)變量,分別叫
userId與userPwd,專(zhuān)門(mén)用來(lái)跟賬號(hào)、密碼輸入框分別做雙向綁定struct Index { // 成員變量列表 userId: string = 'admin' userPwd: string = '123' build() { ........ } }
插播雙向綁定: 即數(shù)據(jù)一旦改變,界面跟著變。 界面輸入內(nèi)容有變化,數(shù)據(jù)也跟著變。
-
那么
ArkTS里如何讓數(shù)據(jù)跟輸入框做雙向綁定呢?(Next版本后新增的語(yǔ)法)TextInput({ text: $$成員變量 }) // 例 TextInput( text: $$this.userId ) -
接下來(lái)讓我們把聲明的
userId與userPwd分別綁定到賬號(hào)框和密碼框TextInput({ placeholder: '賬號(hào)', text: $$this.userId }) .backgroundColor('#fefefe') TextInput({ placeholder: '密碼', text: $$this.userPwd }) .backgroundColor('#fefefe') .type(InputType.Password) -
此時(shí)保存代碼會(huì)看到預(yù)覽器里界面已經(jīng)能顯示綁定的數(shù)據(jù)了,如圖
image.png
-
那我們說(shuō)雙向綁定是:數(shù)據(jù) -> 界面, 同樣,界面的輸入變化也會(huì)影響數(shù)據(jù),那是否能呢?帶著這個(gè)疑問(wèn),我們先給
登錄按鈕加一個(gè)點(diǎn)擊事件,點(diǎn)擊事件里用console.log輸出這兩個(gè)變量的值。Button('登錄') .width('100%') .onClick(() => { console.log(`賬號(hào):${this.userId}, 密碼:${this.userPwd}`) })
注意:這里用到了模板字符串,一些同學(xué)可能不太理解這種字符串。這里說(shuō)明一下:首先是用`這個(gè)符號(hào)包起來(lái),跟單引號(hào)雙引號(hào)都表示字符串,但區(qū)別在于模板字符串能很方便做字符串拼接,例如上面的代碼,相當(dāng)于是 '賬號(hào):' + this.userId + '密碼:' + this.userPwd
-
然后我們?nèi)ヮA(yù)覽起的界面上重新輸入,再點(diǎn)按鈕輸出,看顯示什么(具體看截圖,可以看到在哪看console.log輸出的內(nèi)容)
image.png
-
小結(jié):
- 在輸入框里,使用成員變量前加
$$即可雙向綁定
- 在輸入框里,使用成員變量前加
-
需注意:
- 目前
$$僅能用在基本數(shù)據(jù)類(lèi)型且綁定給內(nèi)置組件
- 目前
裝飾器 - @State
從上面的效果可以看到,已經(jīng)實(shí)現(xiàn)了雙向綁定,但此時(shí)存在一個(gè)問(wèn)題:數(shù)據(jù)無(wú)法再觸發(fā)界面更新
-
例:修改登錄的點(diǎn)擊事件,在里面我修改
userId的值,看界面是否能更新Button('登錄') .width('100%') .onClick(() => { this.userId = '我要變' console.log('新值:' + this.userId) })-
結(jié)果如圖
image.png
-
原因:默認(rèn)聲明的成員變量不具備數(shù)據(jù)改變觸發(fā)界面更新渲染的功能
解決辦法:需要使用裝飾器
-
裝飾器:
修飾某些數(shù)據(jù)、函數(shù),使其具有特殊作用
裝飾器有很多種,本次學(xué)的叫
@State,注意首字母大寫(xiě)-
@State作用:
- 當(dāng)被@State修飾的變量數(shù)據(jù)改變時(shí),UI會(huì)發(fā)生對(duì)應(yīng)的重新渲染。
-
用法
@State 變量: 類(lèi)型 = '初始值'
-
讓我們測(cè)試一下,來(lái)到
登錄案例里找到userId,給它加@State試試@State userId: string = 'admin'
-
效果如下圖:
image.png
但是同樣的,加了裝飾器后會(huì)有輕微的性能開(kāi)銷(xiāo),即使這種開(kāi)銷(xiāo)甚至可以忽略不計(jì)。但是對(duì)于對(duì)性能優(yōu)化有要求的
App而言,則建議。如果數(shù)據(jù)僅僅只是用來(lái)內(nèi)部參與運(yùn)算或臨時(shí)接收界面輸入,不需要將來(lái)重新更新UI,就不加@State裝飾器不光只有
@State,后續(xù)還有很多,學(xué)一個(gè)記一個(gè)。
- 小結(jié):
- 裝飾器
- 修飾數(shù)據(jù)、函數(shù)等,使其具有特殊作用
- @State
- 被@State修飾的變量能當(dāng)它數(shù)據(jù)改變時(shí),UI會(huì)發(fā)生對(duì)應(yīng)的重新渲染
- 裝飾器
實(shí)現(xiàn)登錄功能
最后,我們給
登錄案例收個(gè)尾,當(dāng)用戶(hù)點(diǎn)擊登錄按鈕時(shí),如果輸入的賬號(hào)是admin,密碼是123,則提示登錄成功,否則登錄失敗(將來(lái)學(xué)發(fā)送請(qǐng)求,如今暫時(shí)寫(xiě)死硬判斷)-
代碼如下
Button('登錄') .width('100%') .onClick(() => { if (this.userId === 'admin' && this.userPwd === '123') { promptAction.showToast({ message: '登錄成功' }) } else { promptAction.showToast({ message: '賬號(hào)或密碼錯(cuò)誤' }) } }) -
效果如圖:
image.png
總結(jié)內(nèi)容
本文中我們學(xué)了事件、提示框、成員變量聲明、雙向綁定、裝飾器。我們回顧一下
-
事件:
- on事件名,事件名首字母大寫(xiě),例如:onClick、onChange
-
提示框:
-
需要先導(dǎo)入
import { promptAction } from '@kit.ArkUI' -
然后使用
promptAction.showToast( { message: '提示信息', duration:時(shí)長(zhǎng) } ) 技巧:可以直接輸入promptAction,出提示后,按回車(chē),DevEco會(huì)自動(dòng)導(dǎo)入
-
-
聲明成員變量
變量: 類(lèi)型 = 初始值 默認(rèn)情況下,變量改變不會(huì)觸發(fā)界面重新渲染,因此需要裝飾器:@State
-
雙向綁定
$$this.變量名
課后練習(xí)
- 判斷題:請(qǐng)回答對(duì)或者錯(cuò)
promptAction.showToast方法,傳入duration屬性,值為1000,代表提示框在1秒后消失
成員變量與輸入框雙向綁定時(shí),成員變量前面不用加this
數(shù)據(jù)如果不加@State,就不能進(jìn)行計(jì)算
練習(xí)答案
-
錯(cuò) 2. 錯(cuò) 3. 錯(cuò)
(錯(cuò)錯(cuò)錯(cuò),是我的錯(cuò)。熱戀的時(shí)候怎么不說(shuō),生活的無(wú)奈我已好困惑,你能不能不要再啰嗦)--- 請(qǐng)唱出來(lái)
附加練習(xí)

- 如上圖所示,做一個(gè)年度待辦目標(biāo)的列表。
- 本案例功能比較豐富,各位能做多少做多少。本案例也會(huì)貫穿后面好幾天的教學(xué),所以涉及非常多新知識(shí),做不出來(lái)也正常。
- 提示:打勾部份可以用Image也可以用
Checkbox,如需要做出布局,需要自行根據(jù)文檔預(yù)習(xí)Progress、Stack、List等
互動(dòng)環(huán)節(jié)
- 你覺(jué)得鴻蒙開(kāi)發(fā)跟你以前會(huì)的開(kāi)發(fā),區(qū)別大嗎?歡迎留下你的觀(guān)點(diǎn)。
- 最后,創(chuàng)作不易,請(qǐng)不要吝嗇您的點(diǎn)贊、關(guān)注、收藏、轉(zhuǎn)發(fā)!












