鸿蒙:HarmonyOS控件

对齐

align(Alignment.Center)

设置容器元素绘制区域内的子元素的对齐方式。只在StackButtonMarqueeStepperItemTextTextAreaTextInputFolderStack中生效,其中和文本相关的组件Marquee、Text、TextArea、TextInput的align结果参考===textAlign===。

alignRules

指定设置在相对容器中子组件的对齐规则,===仅当父容器为RelativeContainer时生效===。该方法水平方向上以startend分别替代原方法的leftright,以便在RTL模式下能镜像显示,建议使用该方法指定设置在相对容器中子组件的对齐规则。

alignSelf(ItemAlign.Center)

  • Flex布局中子组件在父容器交叉轴的对齐格式。
  • Blank在父容器交叉轴上设置大小时不会撑满父容器交叉轴,交叉轴不设置大小时alignSelf默认值为ItemAlign.Stretch,会撑满容器交叉轴。
  • GridCol本身也可通过alignSelf(ItemAlign)设置自身对齐方式
  • Row/ColumnalignSelf属性用于控制单个子元素在容器交叉轴上的对齐方式,其优先级高于alignItems属性,如果设置了alignSelf属性,则在单个子元素上会覆盖alignItems属性。

alignItem

设置GridRow中的GridCol垂直主轴方向【交叉轴】对齐方式。GridCol本身也可通过alignSelf(ItemAlign)设置自身对齐方式。当上述两种对齐方式都设置时,以GridCol自身设置为准。

渐变

1
2
3
4
5
6
7
Text("电话")
.fontSize(14)
.fontColor(Color.White)
.linearGradient({
direction: GradientDirection.Bottom,
colors: [[0xFFCFC0, 0.0], [0xFFFFFF, 1]]
})

通过id获取控件

this.id(“具体的控件id”)

通用属性

最小高度

1
2
3
.constraintSize({  
minHeight: 200
})

Text

  • 粗体:.fontWeight(FontWeight.Bold)
  • 省略号:.maxLines(1).textOverflow({overflow: TextOverflow.Ellipsis})
  • 居中:.textAlign(TextAlign.Center)
  • 颜色渐变:
    1
    2
    3
    4
    .linearGradient({  
    direction: GradientDirection.Bottom,
    colors: [[0xFFCFC0, 0.0], [0xFFFFFF, 1]]
    })
  • 可以展示html标签的字符串(支持一小部分)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    controller: TextController = new TextController();

    async getNewsDetail() {
    StyledString.fromHtml(this.detailData.detail_content ?? '')
    .then((styleStr) => { this.controller.setStyledString(styleStr) })
    }


    // 显示属性字符串
    Text(undefined, { controller: this.controller }).layoutWeight(1)
    .onAppear(() => {
    this.getNewsDetail()
    })

Image

objectFit–ImageFit: ^harmonyImage

Contain 保持宽高比进行缩小或者放大,使得图片完全显示在显示边界内。
Cover 保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界。
Auto 图像会根据其自身尺寸和组件的尺寸进行适当缩放,以在保持比例的同时填充视图。
Fill 不保持宽高比进行放大缩小,使得图片充满显示边界。
ScaleDown 保持宽高比显示,图片缩小或者保持不变。
None 保持原有尺寸显示。
TOP_START12+ 图像显示在Image组件的顶部起始端,保持原有尺寸显示。
TOP12+ 图像显示在Image组件的顶部横向居中,保持原有尺寸显示。
TOP_END12+ 图像显示在Image组件的顶部尾端,保持原有尺寸显示。
START12+ 图像显示在Image组件的起始端纵向居中,保持原有尺寸显示。
CENTER12+ 图像显示在Image组件的横向和纵向居中,保持原有尺寸显示。
END12+ 图像显示在Image组件的尾端纵向居中,保持原有尺寸显示。
BOTTOM_START12+ 图像显示在Image组件的底部起始端,保持原有尺寸显示。
BOTTOM12+ 图像显示在Image组件的底部横向居中,保持原有尺寸显示。
BOTTOM_END12+ 图像显示在Image组件的底部尾端,保持原有尺寸显示。

Buttom

Scroll

空白填充组件Blank

Listview组件复用

Listview组件复用注意点:
高度一定要设置,不设置造成复用无法触发

itemview只能有一个封装的view,如下

fold
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Builder  
resumeItemLayout(item: ResumeRow, index: number): void {
ListItem() {
ReuseResumeItemView({
item: item,
isEdit: this.isEdit,
checkList: this.checkList,
resumeViewEnum: this.resumeViewEnum,
onCallClick: (item) => {
SystemUtils.hideKeyBoard()
this.requestLinkWay(item);
},
onChatClick: (item) => {
SystemUtils.hideKeyBoard()
UserInfoUtils.getLoginInfo().then((loginBean) => {
ChatUtils.start2Chat(this.resumeViewEnum === ResumeViewEnum.Search, this._jid, undefined,
loginBean._cid, item._rid, item.expireTime, item.chatSign,
(expireTime, chatSign) => {
item.expireTime = expireTime
item.chatSign = chatSign
})
})
},
onTypeClick: (type) => {
SystemUtils.hideKeyBoard()
item.status = type
this.resumeDatas.notifyDataChange(index)
}
})
.layoutWeight(1)
.reuseId(this.isEdit ? 'hasImage' : 'noImage')

有几种UI样式就用几个reuseId,此处是两种UI样式

子view通过修饰符@Resuable修饰并且将复用时需要的数据通过aboutToReuse方法传入,这样才能在复用的时候数据同步更新

fold
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Reusable  
@Component
export struct ReuseResumeItemView {
@Require @State item: ResumeRow = new ResumeRow()
@State resumeViewEnum: ResumeViewEnum = ResumeViewEnum.Recommend
@Prop isEdit: boolean

build(){
//...
}

aboutToReuse(params: Record<string, ResumeRow | ResumeViewEnum | boolean>) {
this.item = params.item as ResumeRow
this.isEdit = params.isEdit as boolean
this.resumeViewEnum = params.resumeViewEnum as ResumeViewEnum
}
}

aboutToReuse中:被@State修饰的变量需要重新赋值,@Prop修饰的变量可以不用

如果复用view中有子View的情况,类似在这个复用控件中,包含ResumeInvitedView则子view不需要@Resuable修饰,只需要重写aboutToReuse方法即可示例如下

fold
1
2
3
4
5
6
7
8
9
10
11
12
13
@Component  
export struct ResumeInvitedView {
@Require @State item: ResumeRow = new ResumeRow()
onTypeClick?: (type: string) => void

build() {
//...
}

aboutToReuse(params: Record<string, ResumeRow>) {
this.item = params.item as ResumeRow
}
}

Web

展示带html标签的字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import { webview } from '@kit.ArkWeb'  

@Component
struct PublicRecruitisDetailComp {
webviewController: webview.WebviewController = new webview.WebviewController();

//获取数据
async getNewsDetail() {
publicRecruitViewModel.getNewsDetail(this.bean.detail_id ?? '').then((result) => {
this.detailData = result
const detail_content = this.webviewBreak(this.detailData.detail_content ?? '')
//在此处重新加载数据才能显示出来(所以初始化那边的可不用写 .onControllerAttached(() =>{this.webviewController.loadData(encodeURIComponent(detail_content), "text/html", "UTF-8")}))
this.webviewController.loadData(encodeURIComponent(detail_content), "text/html", "UTF-8")
})
}

//处理数据
private webviewBreak(data: string) {
if (data.length > 7 && data.includes("<p>") && data.includes("</p>")) {
data = data.replaceAll("<p>", "<p style=\"word-break:break-all; \">");
}
if (data.length > 14 && data.includes("<table") && data.includes("</table>") &&
!data.includes("style=\"color: #666666;\"")) {
data = data.replaceAll("<table", "<table style=\"color: #666666;\" ");
}
if (data.length > 10 && !data.includes("<div") && !data.includes("</div>")) {
data = "<div style=\"color: #666666; margin: 0; padding: 0; border: 0; \">" + data + "</div>";
}
if (data.length > 6 && !data.includes("<html>")) {
data =
"<html><body>" +
"<head>" +
" <style>" +
" body {" +
" margin: 0;" +
" padding: 0;" +
" font-family: Arial, sans-serif;" +
" color: #666666;" +
" line-height: 1.6;" +
" font-size: 16px;" +
" letter-spacing: 1px;" +
" }" +
" p {" +
" word-break: break-all;" +
" margin-bottom: 1em;" +
" }" +
" table {" +
" margin: 0 auto;" +
" border-collapse: collapse;" +
" width: 95%;" +
" }" +
" td, th {" +
" border: 1px solid #ccc;" +
" padding: 8px;" +
" text-align: center;" +
" letter-spacing: 1px;" +
" }" +
" </style>" +
"</head>"
+ data
+ "</body></html>";
}
return data
}

@Builder
builderDetailContent() {
Row({ space: 15 }) {
Web({ src: "", controller: this.webviewController, renderMode: RenderMode.SYNC_RENDER })
.padding(10)
.javaScriptAccess(true)
.cacheMode(CacheMode.None)
.width(CommonConstants.FULL_LENGTH)
.height(CommonConstants.FULL_LENGTH)
.onAppear(() => {
this.getNewsDetail()
})
}
//...
}

build() {
NavDestination() {
Column() {
Scroll() {
Column({ space: 5 }) {
//...
this.builderDetailContent()
}
}
//...
}
}
//...
}
}