布局/容器组件
Row / Column
对齐方式 VerticalAlign/HorizontalAlign
js
@Entry
@Component
struct Index {
build() {
Row({ space: 10 }) {
Text("row1").height(80).padding({ right: 10, left: 10 }).backgroundColor('red')
Text("row2").height(40).padding({ right: 10, left: 10 }).backgroundColor('green')
Text("row3").padding({ right: 10, left: 10 }).height(50).backgroundColor('black').foregroundColor('white')
}
.alignItems(VerticalAlign.Bottom)
}
}
@Entry
@Component
struct Index {
build() {
Row({ space: 10 }) {
Text("row1").height(80).padding({ right: 10, left: 10 }).backgroundColor('red')
Text("row2").height(40).padding({ right: 10, left: 10 }).backgroundColor('green')
Text("row3").padding({ right: 10, left: 10 }).height(50).backgroundColor('black').foregroundColor('white')
}
.alignItems(VerticalAlign.Bottom)
}
}
排列方式 justifyContent
效果与css的flex布局
js
@Entry
@Component
struct Index {
build() {
Row({ space: 10 }) {
Text("row1").height(80).padding({ right: 10, left: 10 }).backgroundColor('red')
Text("row2").height(40).padding({ right: 10, left: 10 }).backgroundColor('green')
Text("row3").padding({ right: 10, left: 10 }).height(50).backgroundColor('black').foregroundColor('white')
}
.width('100%')
.alignItems(VerticalAlign.Bottom)
.justifyContent(FlexAlign.SpaceBetween)
}
}
@Entry
@Component
struct Index {
build() {
Row({ space: 10 }) {
Text("row1").height(80).padding({ right: 10, left: 10 }).backgroundColor('red')
Text("row2").height(40).padding({ right: 10, left: 10 }).backgroundColor('green')
Text("row3").padding({ right: 10, left: 10 }).height(50).backgroundColor('black').foregroundColor('white')
}
.width('100%')
.alignItems(VerticalAlign.Bottom)
.justifyContent(FlexAlign.SpaceBetween)
}
}
自适应伸缩Blank()
使用Blank()
, 类比swiftui
的spacer()。
尺寸比例拉伸 layoutWeight
例如 .layoutWeight(2)
是 .layoutWeight(1)
宽度2呗
Stack
效果类似SwiftUI的VStack
Flex
js
Flex({
// 主轴
justifyContent: FlexAlign.SpaceBetween,
alignItems: ItemAlign.Center,
wrap: FlexWrap.Wrap
}) {
Text("row1").height(80).padding({ right: 10, left: 10 }).backgroundColor('red')
Text("row2").height(40).padding({ right: 10, left: 10 }).backgroundColor('green')
Text("row3").padding({ right: 10, left: 10 }).height(50).backgroundColor('black').foregroundColor('white')
}
Flex({
// 主轴
justifyContent: FlexAlign.SpaceBetween,
alignItems: ItemAlign.Center,
wrap: FlexWrap.Wrap
}) {
Text("row1").height(80).padding({ right: 10, left: 10 }).backgroundColor('red')
Text("row2").height(40).padding({ right: 10, left: 10 }).backgroundColor('green')
Text("row3").padding({ right: 10, left: 10 }).height(50).backgroundColor('black').foregroundColor('white')
}
RelativeContainer
示例:
js
RelativeContainer() {
Text()
.height(50)
.width(100)
.backgroundColor(Color.Black)
.id("black")
Text(' hi~ ')
.border({ radius: 8 })
.backgroundColor(Color.Orange)
.alignRules({
top: {
'anchor': 'black', 'align': VerticalAlign.Top
},
left: {
'anchor': 'black', 'align': HorizontalAlign.End
}
})
.offset({
left: -10,
top: -6
})
}
RelativeContainer() {
Text()
.height(50)
.width(100)
.backgroundColor(Color.Black)
.id("black")
Text(' hi~ ')
.border({ radius: 8 })
.backgroundColor(Color.Orange)
.alignRules({
top: {
'anchor': 'black', 'align': VerticalAlign.Top
},
left: {
'anchor': 'black', 'align': HorizontalAlign.End
}
})
.offset({
left: -10,
top: -6
})
}
展示效果:
GridRow、GridCol
使用栅格的默认列数12列(可以通过columns属性修改)。默认有断点规则,可以根据不同的屏幕宽度定制col的宽度。
js
@Entry
@Component
struct Index {
@State bgColors: Color[] =
[Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Pink, Color.Grey, Color.Blue, Color.Brown];
build() {
GridRow() {
ForEach(this.bgColors, (color: Color, index?: number | undefined) => {
GridCol({
// span: 2 // 固定比例
// 动态比例
span: {
xs: 2, // 在最小宽度类型设备上,栅格子组件占据的栅格容器2列。
sm: 3, // 在小宽度类型设备上,栅格子组件占据的栅格容器3列。
md: 4, // 在中等宽度类型设备上,栅格子组件占据的栅格容器4列。
lg: 6, // 在大宽度类型设备上,栅格子组件占据的栅格容器6列。
xl: 8, // 在特大宽度类型设备上,栅格子组件占据的栅格容器8列。
xxl: 12 // 在超大宽度类型设备上,栅格子组件占据的栅格容器12列。
}
}) {
Row() {
Text(`${index}`)
}.width("100%").height('50vp')
}.backgroundColor(color)
})
}
}
}
@Entry
@Component
struct Index {
@State bgColors: Color[] =
[Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Pink, Color.Grey, Color.Blue, Color.Brown];
build() {
GridRow() {
ForEach(this.bgColors, (color: Color, index?: number | undefined) => {
GridCol({
// span: 2 // 固定比例
// 动态比例
span: {
xs: 2, // 在最小宽度类型设备上,栅格子组件占据的栅格容器2列。
sm: 3, // 在小宽度类型设备上,栅格子组件占据的栅格容器3列。
md: 4, // 在中等宽度类型设备上,栅格子组件占据的栅格容器4列。
lg: 6, // 在大宽度类型设备上,栅格子组件占据的栅格容器6列。
xl: 8, // 在特大宽度类型设备上,栅格子组件占据的栅格容器8列。
xxl: 12 // 在超大宽度类型设备上,栅格子组件占据的栅格容器12列。
}
}) {
Row() {
Text(`${index}`)
}.width("100%").height('50vp')
}.backgroundColor(color)
})
}
}
}
@ohos.mediaquery
略
List
js
@Entry
@Component
struct Index {
@State bgColors: Color[] =
[Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Pink, Color.Grey, Color.Blue, Color.Brown];
build() {
List() {
ForEach(this.bgColors, (item: Color, idx) => {
ListItem() {
Text('' + idx)
.textAlign(TextAlign.Center)
.foregroundColor(Color.Black)
.width(100)
.height(40)
.backgroundColor(item)
}
})
}
.backgroundColor(Color.Black)
.listDirection(Axis.Vertical)
.alignListItem(ListItemAlign.Center)
}
}
@Entry
@Component
struct Index {
@State bgColors: Color[] =
[Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Pink, Color.Grey, Color.Blue, Color.Brown];
build() {
List() {
ForEach(this.bgColors, (item: Color, idx) => {
ListItem() {
Text('' + idx)
.textAlign(TextAlign.Center)
.foregroundColor(Color.Black)
.width(100)
.height(40)
.backgroundColor(item)
}
})
}
.backgroundColor(Color.Black)
.listDirection(Axis.Vertical)
.alignListItem(ListItemAlign.Center)
}
}
其他能力:
- 间距 space
- 分割线
- 粘性标题
- 分组列表
- 支持滚动到某位置
- 支持响应滚动位置
- 侧滑支持
- 下拉刷新与上拉加载
Grid
略
Swiper
略
UI组件
@CustomDialog
CustomDialog装饰器用于装饰自定义弹框,此装饰器内进行自定义内容(也就是弹框内容)。
示例:
js
@Entry
@Component
struct Index {
dialogController: CustomDialogController = new CustomDialogController({
builder: CustomDialogExample(),
})
build() {
Column() {
Button() {
Text("open dialog")
.foregroundColor(Color.White)
.padding(20)
}
.onClick(() => {
this.dialogController.open()
})
}
}
}
@CustomDialog
struct CustomDialogExample {
controller: CustomDialogController = new CustomDialogController({
builder: CustomDialogExample({}),
})
cancel?: () => void
confirm?: () => void
build() {
Column() {
Text('我是内容').fontSize(20).margin({ top: 10, bottom: 10 })
Row() {
Blank()
Button('cancel')
.onClick(() => {
this.controller.close()
if (this.cancel) {
this.cancel()
}
}).backgroundColor(0xffffff).fontColor(Color.Black)
Blank()
Button('confirm')
.onClick(() => {
this.controller.close()
if (this.confirm) {
this.confirm()
}
}).backgroundColor(0xffffff).fontColor(Color.Red)
Blank()
}.width('100%')
}
}}
@Entry
@Component
struct Index {
dialogController: CustomDialogController = new CustomDialogController({
builder: CustomDialogExample(),
})
build() {
Column() {
Button() {
Text("open dialog")
.foregroundColor(Color.White)
.padding(20)
}
.onClick(() => {
this.dialogController.open()
})
}
}
}
@CustomDialog
struct CustomDialogExample {
controller: CustomDialogController = new CustomDialogController({
builder: CustomDialogExample({}),
})
cancel?: () => void
confirm?: () => void
build() {
Column() {
Text('我是内容').fontSize(20).margin({ top: 10, bottom: 10 })
Row() {
Blank()
Button('cancel')
.onClick(() => {
this.controller.close()
if (this.cancel) {
this.cancel()
}
}).backgroundColor(0xffffff).fontColor(Color.Black)
Blank()
Button('confirm')
.onClick(() => {
this.controller.close()
if (this.confirm) {
this.confirm()
}
}).backgroundColor(0xffffff).fontColor(Color.Red)
Blank()
}.width('100%')
}
}}
额外的:
- 支持修改样式
- 支持弹窗嵌套
- 支持设置动画
富文本字符串
参考 属性字符串(StyledString/MutableStyledString
组件封装优化
[分层架构与模块化设计 、ArkUI组件封装与UI动态操作及解决方案(https://developer.huawei.com/consumer/cn/training/course/live/C101719310316478739)
AttributeModifier
Q&A
如何根据设计图进行开发
- vp 屏幕密度相关像素,根据屏幕像素密度转换为屏幕物理像素,当数值不带单位时,默认单位vp。
1vp约等于3px?vp与px的比例与屏幕像素密度有关。 - px 屏幕物理像素单位
- fp 字体像素,与vp类似适用屏幕密度变化,随系统字体大小设置变化。
- lpx 视窗逻辑像素单位,lpx单位为实际屏幕宽度与逻辑宽度(通过designWidth配置)的比值,designWidth默认值为720。当designWidth为720时,在实际宽度为1440物理像素的屏幕上,1lpx为2px大小。
$r(20fp)
开发建议根据设计图基准设置好designWidth,使用lpx。 其他单位系统提供的函数转换。
修改main_pages.json
文件:
json
{
"src": [
"pages/Index",
"pages/Second"
],
"window": {
"designWidth": 720,
"autoDesignWidth": false
}
}
{
"src": [
"pages/Index",
"pages/Second"
],
"window": {
"designWidth": 720,
"autoDesignWidth": false
}
}
演示:
js
Column() {
Text(`当前:1vp = ${vp2px(1)}px \n width=260vp`)
.foregroundColor(Color.White)
.height(70)
.width("260vp")
.backgroundColor(Color.Black)
.margin({ bottom: 10 })
// 预览模式显示为0,真机模式显示正常
Text(`当前:1lpx = ${lpx2px(1)}px \n width=360lpx`)
.height(70)
.width("360lpx")
.backgroundColor(Color.Black)
.margin({ bottom: 10 })
Text(`width = 50%`)
.height(70)
.width("50%")
.backgroundColor(Color.Black)
}
.alignItems(HorizontalAlign.Start)
.foregroundColor(Color.White)
Column() {
Text(`当前:1vp = ${vp2px(1)}px \n width=260vp`)
.foregroundColor(Color.White)
.height(70)
.width("260vp")
.backgroundColor(Color.Black)
.margin({ bottom: 10 })
// 预览模式显示为0,真机模式显示正常
Text(`当前:1lpx = ${lpx2px(1)}px \n width=360lpx`)
.height(70)
.width("360lpx")
.backgroundColor(Color.Black)
.margin({ bottom: 10 })
Text(`width = 50%`)
.height(70)
.width("50%")
.backgroundColor(Color.Black)
}
.alignItems(HorizontalAlign.Start)
.foregroundColor(Color.White)
参考:
像素单位-ArkTS组件-ArkUI(方舟UI框架)-应用框架 - 华为HarmonyOS开发者
尺寸设置-通用属性-组件通用信息-ArkTS组件-ArkUI(方舟UI框架)-应用框架 - 华为HarmonyOS开发者
持久化存储示例
用户偏好设举例:
js
import { preferences } from '@kit.ArkData';
saveCache(key: string, value: string, dbName: string = 'myStore') {
let preference = preferences.getPreferences(getContext(this), dbName)
.then(async (p) => {
await p.put(key, value)
p.flush()
}).catch((err: Error) => {
console.error('put the preferences failed, err: ' + err)
});
}
getCache(key: string, dbName: string = 'myStore') {
let preference = preferences.getPreferences(getContext(this), dbName)
.then(async (p) => {
const result = await p.get(key, '') as string
console.log(result)
})
}
import { preferences } from '@kit.ArkData';
saveCache(key: string, value: string, dbName: string = 'myStore') {
let preference = preferences.getPreferences(getContext(this), dbName)
.then(async (p) => {
await p.put(key, value)
p.flush()
}).catch((err: Error) => {
console.error('put the preferences failed, err: ' + err)
});
}
getCache(key: string, dbName: string = 'myStore') {
let preference = preferences.getPreferences(getContext(this), dbName)
.then(async (p) => {
const result = await p.get(key, '') as string
console.log(result)
})
}
是否需要请求网络权限
新版似乎不需要请求网络权限了。
json
// module.json5
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:network_dependency_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
}
],
// module.json5
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:network_dependency_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
}
],
列表的上拉刷新下拉加载
参考示例:新闻数据加载