鴻蒙應(yīng)用開(kāi)發(fā)從入門(mén)到入行 - 篇3:ArkUI布局基礎(chǔ)與制作可交互頁(yè)面

第三篇 - 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)效果如下:

image.png
  • 此時(shí)大家發(fā)現(xiàn)兩個(gè)問(wèn)題:

    1. 明明短信驗(yàn)證碼那一行的Row給了justifyContent(FlexAlign.SpaceBetween),但沒(méi)生效,看著還是居中。為什么呢?
    2. 所有內(nèi)容行與行之間沒(méi)有間距,導(dǎo)致挨的太緊。
  • 這兩個(gè)問(wèn)題都很好很解決,分別如下

    1. 給了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%')即可

    2. 行與行之間要設(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
  • 那分析完加父容器后,還有個(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í)候有兩種解決辦法:

    1. 給兩個(gè)TextInput、Row、Button這四行設(shè)置左右外間距
    2. 給他們共同的父組件設(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é)廢了嗎?

image.png
  • 本案例新知識(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
    });
    

技巧:可以在寫(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: '貓林老師教程真好!' })
    
    • 效果如下
image.png

聲明組件內(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è)變量,分別叫userIduserPwd,專(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)讓我們把聲明的userIduserPwd分別綁定到賬號(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ò)
    1. promptAction.showToast方法,傳入duration屬性,值為1000,代表提示框在1秒后消失

    2. 成員變量與輸入框雙向綁定時(shí),成員變量前面不用加this

    3. 數(shù)據(jù)如果不加@State,就不能進(jìn)行計(jì)算

練習(xí)答案

  1. 錯(cuò) 2. 錯(cuò) 3. 錯(cuò)

    (錯(cuò)錯(cuò)錯(cuò),是我的錯(cuò)。熱戀的時(shí)候怎么不說(shuō),生活的無(wú)奈我已好困惑,你能不能不要再啰嗦)--- 請(qǐng)唱出來(lái)

附加練習(xí)

image.png
  • 如上圖所示,做一個(gè)年度待辦目標(biāo)的列表。
  • 本案例功能比較豐富,各位能做多少做多少。本案例也會(huì)貫穿后面好幾天的教學(xué),所以涉及非常多新知識(shí),做不出來(lái)也正常。
  • 提示:打勾部份可以用Image也可以用Checkbox,如需要做出布局,需要自行根據(jù)文檔預(yù)習(xí)Progress、StackList

互動(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ā)!
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容