這個(gè)結(jié)構(gòu)和Flutter阿、Compose阿挺像,順?biāo)兄?,都為生命式UI方式,看下組件吧:


1. 組件介紹:
@Entry
代表一個(gè)界面的入口,跟Activity聲明有點(diǎn)像
@Component
代表是一個(gè)UI布局的結(jié)構(gòu)體聲明,下面跟結(jié)構(gòu)體
結(jié)構(gòu)體內(nèi),組件在build方法內(nèi),每個(gè)組件都必須實(shí)現(xiàn)build方法,是用于定義組件的聲明式UI描述
2. 基礎(chǔ)組件

基礎(chǔ)組件的聲明式使用,參數(shù)方式傳遞,參數(shù)既可以用直接輸入、也可以來源于資源(當(dāng)然正常編碼都用資源啦,涉及到多語言等自動(dòng)適配啦)
Text
Column() {
Text(this.message)//外部文字定義使用
.fontSize('36.00fp')
.fontWeight(FontWeight.Bold)
Text("一段文字")//直接字符串
.fontColor("#ff00ff")
Text($r("app.string.EntryAbility_label"))//string資源使用
.fontColor($r('app.color.main_text_color'))//color資源使用
}
Button
Column() {
Button("button", { type: ButtonType.Normal })//矩形
Button("button", { type: ButtonType.Capsule })//默認(rèn)的膠囊型
}
容器:

Colume:列容器
| 接口 | 參數(shù)說明 |
|---|---|
| alignItems | 水平對(duì)齊:HorizontalAlign枚舉,默認(rèn)Center |
| justifyContent | 垂直對(duì)齊:FlexAlign枚舉,默認(rèn)Top |
Column() {
Text('Text1')
.width(fp2px(50))
.height(fp2px(50))
.backgroundColor(Color.White)
.textAlign(TextAlign.Center)
Text('Text2')
.width(fp2px(50))
.height(fp2px(50))
.backgroundColor(Color.White)
.textAlign(TextAlign.Center)
Text('Text3')
.width(fp2px(50))
.height(fp2px(50))
.backgroundColor(Color.White)
.textAlign(TextAlign.Center)
}
.width('100%')
.height('100%')
.backgroundColor(Color.Green)
.alignItems(HorizontalAlign.End)
.justifyContent(FlexAlign.End)
Row:行容器
| 接口 | 參數(shù)說明 |
|---|---|
| alignItems | 垂直對(duì)齊:VerticalAlign枚舉,默認(rèn)Center |
| justifyContent | 水平對(duì)齊:FlexAlign枚舉,默認(rèn)Start |
Row() {
Text('Text1')
.width(fp2px(50))
.height(fp2px(50))
.backgroundColor(Color.White)
.textAlign(TextAlign.Center)
Text('Text2')
.width(fp2px(50))
.height(fp2px(50))
.backgroundColor(Color.White)
.textAlign(TextAlign.Center)
Text('Text3')
.width(fp2px(50))
.height(fp2px(50))
.backgroundColor(Color.White)
.textAlign(TextAlign.Center)
}
.width('100%')
.height('100%')
.backgroundColor(Color.Green)
.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Start)
Stack:棧容器
堆疊型容器
| 接口 | 參數(shù)說明 |
|---|---|
| alignContent | 對(duì)齊:Alignment,默認(rèn)是Center |
Stack(/*{ alignContent: Alignment.TopStart }*/) {
Text('Text1')
.width(fp2px(50))
.height(fp2px(50))
.backgroundColor(Color.Orange)
.textAlign(TextAlign.Center)
Text('Text2')
.width(fp2px(30))
.height(fp2px(30))
.backgroundColor(Color.Red)
.textAlign(TextAlign.Center)
Text('Text3')
.width(fp2px(20))
.height(fp2px(20))
.backgroundColor(Color.Yellow)
.textAlign(TextAlign.Center)
}
.width('100%')
.height('100%')
.backgroundColor(Color.Green)
如下圖:后面的覆蓋在前面的上面

練習(xí):靜態(tài)水果排行榜頁(yè)面
build() {
Column() {
/*頂部標(biāo)題欄*/
Row() {
/*左側(cè)*/
Row() {
Image($r('app.media.ic_public_back'))
.width(22)
.height(22)
.margin({ right: 18 })
Text("排行榜")
.fontSize(20)
}
.width('50%')
.height(100)
.justifyContent(FlexAlign.Start)
/*右側(cè)*/
Row() {
Image($r('app.media.loading'))
.width(22)
.height(22)
}
.width('50%')
.height(100)
.justifyContent(FlexAlign.End) //右對(duì)齊
}
.width('100%')
.height(47)
.padding({ left: 26, right: 26 })
.margin({ top: 18 })
/*水果排行榜標(biāo)題*/
Row() {
Text("排名")
.fontSize(14)
.width('30%')
.fontColor('#989a9c')
Text("種類")
.fontSize(14)
.width('50%')
.fontColor('#989a9c')
Text("得票數(shù)")
.fontSize(14)
.width('20%')
.fontColor('#989a9c')
}
.width('90%')
.padding(15)
/*水果排行內(nèi)容*/
Column() {
List({space: 10}) {
ListItem() {
Row() {
Text("1")
.width('30%')
Text("蘋果")
.width('50%')
Text("12080")
.width('20%')
}
}
ListItem() {
Row() {
Text("2")
.width('30%')
Text("葡萄")
.width('50%')
Text("10320")
.width('20%')
}
}
ListItem() {
Row() {
Text("3")
.width('30%')
Text("西瓜")
.width('50%')
Text("9801")
.width('20%')
}
}
}
.width('90%')
.padding(15)
}
}
.backgroundColor("#F1F3F5")
.width('100%')
.height('100%')
}
還是有點(diǎn)像模像樣的啦: 布局拆解后,按橫向縱向排列布局處理,響應(yīng)式寫法確實(shí)帥。就是括號(hào)嵌套層級(jí)有點(diǎn)多,看起來有點(diǎn)難受,看下效果

練習(xí):動(dòng)態(tài)渲染數(shù)據(jù)
靜態(tài)的每個(gè)自己添加在實(shí)際使用的時(shí)候是不切實(shí)際的,肯定是根據(jù)數(shù)據(jù)進(jìn)行遍歷添加。試一下foreach
/**
* 水果排行數(shù)組
*/
fruits: Array<Object> = [{
id: '1',
name: "蘋果",
vote: "12080"
}, {
id: '2',
name: "葡萄",
vote: "10320"
}, {
id: '3',
name: "西瓜",
vote: "9801"
}, {
id: '4',
name: "香蕉",
vote: "7546"
}, {
id: '5',
name: "菠蘿",
vote: "1208"
}]
/**
* 水果排行榜頁(yè)面 靜態(tài)練習(xí)
*/
build() {
Column() {
/*頂部標(biāo)題欄*/
Row() {
/*左側(cè)*/
Row() {
Image($r('app.media.ic_public_back'))
.width(22)
.height(22)
.margin({ right: 18 })
Text("排行榜")
.fontSize(20)
}
.width('50%')
.height(100)
.justifyContent(FlexAlign.Start)
/*右側(cè)*/
Row() {
Image($r('app.media.loading'))
.width(22)
.height(22)
}
.width('50%')
.height(100)
.justifyContent(FlexAlign.End) //右對(duì)齊
}
.width('100%')
.height(47)
.padding({ left: 26, right: 26 })
.margin({ top: 18 })
/*水果排行榜標(biāo)題*/
Row() {
Text("排名")
.fontSize(14)
.width('30%')
.fontColor('#989a9c')
Text("種類")
.fontSize(14)
.width('50%')
.fontColor('#989a9c')
Text("得票數(shù)")
.fontSize(14)
.width('20%')
.fontColor('#989a9c')
}
.width('90%')
.padding(15)
/*水果排行內(nèi)容*/
Column() {
List({ space: 10 }) {
/*采用對(duì)象來*/
ForEach(this.fruits, (item) => {
ListItem() {
Row() {
if (item.id <= 3) {
Column() {
Row() {
Text(item.id)
.fontSize(14)
.fontColor(Color.White)
}
.width(24)
.height(24)
.borderRadius(18)
.backgroundColor(Color.Blue)
.justifyContent(FlexAlign.Center) //水平對(duì)齊
}
.width('30%')
.alignItems(HorizontalAlign.Start) //水平對(duì)齊
}
else {
Column() {
Text(item.id)
.fontSize(14)
.textAlign(TextAlign.Center)//文字居中,對(duì)應(yīng)上面的Row的水平居中
.width(24)
.height(24)
}
.width('30%')
.alignItems(HorizontalAlign.Start) //水平對(duì)齊
}
Text(item.name)
.width('50%')
Text(item.vote)
.width('20%')
}
}
})
}
.width('90%')
.padding(15)
}
}
.backgroundColor("#F1F3F5")
.width('100%')
.height('100%')
}
主要關(guān)鍵修改是增加了一個(gè)數(shù)組Array,然后使用foreach來遍歷產(chǎn)生ListItem
效果:

組件事件修飾器
- onclick事件
Button("點(diǎn)擊")
.onClick(event => {
this.count++
console.log("點(diǎn)擊事件觸發(fā):" + this.count)
})
- @State修飾器
我們希望對(duì)應(yīng)值修改后能體現(xiàn)在UI上則可以用改狀態(tài)修飾器修飾對(duì)應(yīng)的值定義
@State count: number = 0
#在count變化后, Text控件自動(dòng)會(huì)刷新
Text("文字" + this.count)
@Link 修飾器
通過@link修飾器可以將子組件的變量與父組件的變量建立關(guān)聯(lián)關(guān)系@Builder 修飾器
可以將組件抽象提取為函數(shù),重復(fù)UI利用,在build里調(diào)用
3. 模塊化
數(shù)據(jù)與頁(yè)面分離:
將頁(yè)面和數(shù)據(jù)分離開到不同文件,可以方便管理代碼
(1)另建ets文件
(2)將類類型、靜態(tài)數(shù)據(jù)聲明放到獨(dú)立的ets中
(3)將類型、數(shù)據(jù)聲明加export,讓外部可以使用
(4)在需要使用的地方,使用import ··· from 方式來引用
如下:

布局分離:
一個(gè)etx中有多個(gè)view時(shí),可以拆分成不同的自定義view,或者說組合多個(gè)自定義view到布局:
- 新建etx文件,使用 子組件方式,修飾結(jié)構(gòu)體
@Component
export struct TitleComponent{
//這樣實(shí)現(xiàn)build方法即可
build(){
}
}
- 對(duì)于子組件中有變量要與父控件保持一致時(shí),使用前面說的@Link,建議關(guān)聯(lián)父控件值時(shí),名稱也要與父控件聲明一樣(不一樣也沒啥,傳參綁定)
@Component
struct TitleComponent{
//關(guān)聯(lián)父控件中傳入變量的值
@Link isSwitchFruits:boolean
build(){
Row() {
//左側(cè)
Row() {
Image($r('app.media.ic_public_back'))
.width(22)
.height(22)
.margin({ right: 18 })
Text("排行榜")
.fontSize(20)
}
.width('50%')
.height(100)
.justifyContent(FlexAlign.Start)
//右側(cè)
Row() {
Image($r('app.media.loading'))
.width(22)
.height(22)
.onClick(() => {
this.isSwitchFruits = !this.isSwitchFruits
})
}
.width('50%')
.height(100)
.justifyContent(FlexAlign.End) //右對(duì)齊
}
.width('100%')
.height(47)
.padding({ left: 26, right: 26 })
.margin({ top: 18 })
}
}
使用時(shí):
//引用
import {TitleComponent} from '../view/TitleComponent'
//使用傳參方式綁定子控件內(nèi)容,左側(cè)為子控件變量,右側(cè)用取值方式取當(dāng)前要傳的值
TitleComponent({sisSwitchFruits:$isSwitchFruits})
- 對(duì)于可以重復(fù)利用的使用@Builder
/**
* 水果排行榜頁(yè)面 靜態(tài)練習(xí)
*/
build() {
Column() {
/*頂部標(biāo)題欄*/
//使用傳參方式綁定子空間內(nèi)容
TitleComponent({ sisSwitchFruits: $isSwitchFruits })
//水果排行榜標(biāo)題
ListTitleComponent()
/*水果排行內(nèi)容*/
this.RankList()
}
.backgroundColor("#F1F3F5")
.width('100%')
.height('100%')
}
/**
* UI做builder聲明方式,UI組件作為函數(shù)使用,被build內(nèi)調(diào)用
*/
@Builder RankList() {
Column() {
List({ space: 10 }) {
//采用對(duì)象來
ForEach(this.isSwitchFruits ? this.fruits : this.fruitsData2, (item: Fruit) => {
ListItem() {
ItemComponent({
itemId: item.id,
itemName: item.name,
itemVote: item.vote
})
}
})
}
.width('90%')
.padding(15)
}
}
至此,簡(jiǎn)單的布局就可以啦:
1.簡(jiǎn)單的組件使用布局:Column、Row、Stack、ListItem
2.狀態(tài)改變等:@State 來關(guān)注變化、@link 來子控件關(guān)聯(lián)父控件變量、
3.將view獨(dú)立:獨(dú)立函數(shù)@Builder抽離view到函數(shù),獨(dú)立文件 @Component 和 import、from