基本概念
- UI: 即用户界面。开发者可以将应用的用户界面设计为多个功能页面,每个页面进行单独的文件管理,并通过页面路由API完成页面间的调度管理如跳转、回退等操作,以实现应用内的功能解耦。
- 组件: UI构建与显示的最小单位,如列表、网格、按钮、单选框、进度条、文本等。开发者通过多种组件的组合,构建出满足自身应用诉求的完整界面。
两种开发范式
- 声明式开发范式:采用基于TypeScript声明式UI语法扩展而来的ArkTS语言,从组件、动画和状态管理三个维度提供UI绘制能力。
- 类Web开发范式:采用经典的HML、CSS、JavaScript三段式开发方式,即使用HML标签文件搭建布局、使用CSS文件描述样式、使用JavaScript文件处理逻辑。该范式更符合于Web前端开发者的使用习惯,便于快速将已有的Web应用改造成方舟UI框架应用。
推荐声明式开发,1是harmony主推。2是性能更佳
ArkTS声明式开发范式
本文主推声明式开发,因为这是harmony主推的一种方式,之后的更新重点也更偏向于这边
基本概念
在编写UI时,我们主要应该关注于布局,组件,路由,交互事件 等,这是开发出一个页面,并进行一些简单交互所需要的
Harmony的开发有一些特点是需要我们前期去关注的,这会帮助我们更好的进行开发:
- 数据驱动UI变化:让开发者更专注自身业务逻辑的处理。当UI发生变化时,开发者无需编写在不同的UI之间进行切换的UI代码, 开发人员仅需要编写引起界面变化的数据,具体UI如何变化交给框架。
- UI在build函数中进行编写,布局,交互等。所以我们只需要关注build函数就可以看到整一个布局。类似于html
布局
这是一张常见的布局结构图,我们在编写代码的时候即可根据这样的布局进行开发。主要得上手试,试多几遍之后就了解了
常见的几种布局
布局组件这里大概介绍了9种常见的布局,对于熟悉CSS的人也都知道这几种布局的具体概念,展开来说太过繁琐。需要用到布局可以通过下方的链接去往官方文档中查看布局使用方式。
| 布局 | 应用场景 |
|---|---|
| developer.huawei.com/consumer/cn… | 如果布局内子元素超过1个时,且能够以某种方式线性排列时优先考虑此布局。 |
| developer.huawei.com/consumer/cn… | 组件需要有堆叠效果时优先考虑此布局。层叠布局的堆叠效果不会占用或影响其他同容器内子组件的布局空间。例如developer.huawei.com/consumer/cn… |
| developer.huawei.com/consumer/cn… | 弹性布局是与线性布局类似的布局方式。区别在于弹性布局默认能够使子组件压缩或拉伸。在子组件需要计算拉伸或压缩比例时优先使用此布局,可使得多个容器内子组件能有更好的视觉上的填充效果。 |
| developer.huawei.com/consumer/cn… | 相对布局是在二维空间中的布局方式,不需要遵循线性布局的规则,布局方式更为自由。通过在子组件上设置锚点规则(AlignRules)使子组件能够将自己在横轴、纵轴中的位置与容器或容器内其他子组件的位置对齐。设置的锚点规则可以天然支持子元素压缩、拉伸、堆叠或形成多行效果。在页面元素分布复杂或通过线性布局会使容器嵌套层数过深时推荐使用。 |
| developer.huawei.com/consumer/cn… | 栅格是多设备场景下通用的辅助定位工具,可将空间分割为有规律的栅格。栅格不同于网格布局固定的空间划分,可以实现不同设备下不同的布局,空间划分更随心所欲,从而显著降低适配不同屏幕尺寸的设计及开发成本,使得整体设计和开发流程更有秩序和节奏感,同时也保证多设备上应用显示的协调性和一致性,提升用户体验。推荐内容相同但布局不同时使用。 |
| developer.huawei.com/consumer/cn… | 媒体查询可根据不同设备类型或同设备不同状态修改应用的样式。例如根据设备和应用的不同属性信息设计不同的布局,以及屏幕发生动态改变时更新应用的页面布局。 |
| developer.huawei.com/consumer/cn… | 使用列表可以高效地显示结构化、可滚动的信息。在ArkUI中,列表具有垂直和水平布局能力和自适应交叉轴方向上排列个数的布局能力,超出屏幕时可以滚动。列表适合用于呈现同类数据类型或数据类型集,例如图片和文本。 |
| developer.huawei.com/consumer/cn… | 网格布局具有较强的页面均分能力、子元素占比控制能力。网格布局可以控制元素所占的网格数量、设置子元素横跨几行或者几列,当网格容器尺寸发生变化时,所有子元素以及间距等比例调整。推荐在需要按照固定比例或者均匀分配空间的布局场景下使用,例如计算器、相册、日历等。 |
| developer.huawei.com/consumer/cn… | 轮播组件通常用于实现广告轮播、图片预览等。 |
| developer.huawei.com/consumer/cn… | 选项卡可以在一个页面内快速实现视图内容的切换,一方面提升查找信息的效率,另一方面精简用户单次获取到的信息量。 |
布局位置
position、offset等属性影响了布局容器相对于自身或其他组件的位置。
| 定位能力 | 使用场景 | 实现方式 |
|---|---|---|
| 绝对定位 | 对于不同尺寸的设备,使用绝对定位的适应性会比较差,在屏幕的适配上有缺陷。 | 使用developer.huawei.com/consumer/cn… |
| 相对定位 | 相对定位不脱离文档流,即原位置依然保留,不影响元素本身的特性,仅相对于原位置进行偏移。 | 使用developer.huawei.com/consumer/cn… |
对子元素的约束
| 对子元素的约束能力 | 使用场景 | 实现方式 |
|---|---|---|
| 拉伸 | 容器组件尺寸发生变化时,增加或减小的空间全部分配给容器组件内指定区域。 | developer.huawei.com/consumer/cn… |
- flexGrow基于父容器的剩余空间分配来控制组件拉伸。
- flexShrink设置父容器的压缩尺寸来控制组件压缩。 | | 缩放 | 子组件的宽高按照预设的比例,随容器组件发生变化,且变化过程中子组件的宽高比不变。 | developer.huawei.com/consumer/cn… | | 占比 | 占比能力是指子组件的宽高按照预设的比例,随父容器组件发生变化。 | 基于通用属性的两种实现方式:
- 将子组件的宽高设置为父组件宽高的百分比。 2. developer.huawei.com/consumer/cn… | | 隐藏 | 隐藏能力是指容器组件内的子组件,按照其预设的显示优先级,随容器组件尺寸变化显示或隐藏,其中相同显示优先级的子组件同时显示或隐藏。 | 通过developer.huawei.com/consumer/cn… |
RelativeContainer(相对布局)
容器内部的子元素支持设置相对位置关系,即子元素2可以相对于子元素1来进行位置定位,被相对定位的元素称为锚点。子元素可以支持兄弟元素,父容器作为锚点
父元素定位
__container__代表父容器的ID
build() {
RelativeContainer() {
Text('hello world')
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
}
.height('100%')
.width('100%')
}
VerticalAlign: 垂直对齐 value: Top Center Bottom
HorizontalAlign: 水平对齐 value: Start Center End
以兄弟元素为锚点
let AlignRus:Record<string,Record<string,string|VerticalAlign|HorizontalAlign>> = {
'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },
'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start }
}
let RelConB:Record<string,Record<string,string|VerticalAlign|HorizontalAlign>> = {
'top': { 'anchor': 'row1', 'align': VerticalAlign.Bottom },
'left' : { 'anchor': 'row1', 'align': HorizontalAlign.Start }
}
let Mleft:Record<string,number> = { 'left': 20 }
let BWC:Record<string,number|string> = { 'width': 2, 'color': '#6699FF' }
RelativeContainer() {
Row().width(100).height(100)
.backgroundColor("#FF3333")
.alignRules(AlignRus)
.id("row1")
Row().width(100).height(100)
.backgroundColor("#FFCC00")
.alignRules(RelConB)
.id("row2")
}.width(300).height(300)
.margin(Mleft)
.border(BWC)
Flex
布局方向:
FlexDirection.Row(默认值):主轴为水平方向,子元素从起始端沿着水平方向开始排布。
FlexDirection.RowReverse:主轴为水平方向,子元素从终点端沿着FlexDirection. Row相反的方向开始排布。
FlexDirection.Column:主轴为垂直方向,子元素从起始端沿着垂直方向开始排布。
FlexDirection.ColumnReverse:主轴为垂直方向,子元素从终点端沿着FlexDirection. Column相反的方向开始排布。
布局换行:
FlexWrap. NoWrap(默认值):不换行。如果子元素的宽度总和大于父元素的宽度,则子元素会被压缩宽度。
FlexWrap. Wrap:换行,每一行子元素按照主轴方向排列。
FlexWrap. WrapReverse:换行,每一行子元素按照主轴反方向排列。
主轴对齐方式
FlexAlign.Start(默认值):子元素在主轴方向起始端对齐, 第一个子元素与父元素边沿对齐,其他元素与前一个元素对齐。
FlexAlign.Center:子元素在主轴方向居中对齐。
FlexAlign.End:子元素在主轴方向终点端对齐, 最后一个子元素与父元素边沿对齐,其他元素与后一个元素对齐。
FlexAlign.SpaceBetween:Flex主轴方向均匀分配弹性元素,相邻子元素之间距离相同。第一个子元素和最后一个子元素与父元素边沿对齐。
FlexAlign.SpaceAround:Flex主轴方向均匀分配弹性元素,相邻子元素之间距离相同。第一个子元素到主轴起始端的距离和最后一个子元素到主轴终点端的距离是相邻元素之间距离的一半。
FlexAlign.SpaceEvenly:Flex主轴方向元素等间距布局,相邻子元素之间的间距、第一个子元素与主轴起始端的间距、最后一个子元素到主轴终点端的间距均相等。
交叉轴对齐方式
ItemAlign.Auto:使用Flex容器中默认配置。
ItemAlign.Start:交叉轴方向首部对齐。
ItemAlign.Center:交叉轴方向居中对齐。
ItemAlign.End:交叉轴方向底部对齐。
ItemAlign.Stretch:交叉轴方向拉伸填充,在未设置尺寸时,拉伸到容器尺寸。
ItemAlign. Baseline:交叉轴方向文本基线对齐。
使用方式
Flex({ direction: FlexDirection.ColumnReverse }) {
Text('1').width('100%').height(50).backgroundColor(0xF5DEB3)
Text('2').width('100%').height(50).backgroundColor(0xD2B48C)
Text('3').width('100%').height(50).backgroundColor(0xF5DEB3)
}
子元素设置交叉轴对齐
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) { // 容器组件设置子元素居中
Text('alignSelf Start').width('25%').height(80)
.alignSelf(ItemAlign.Start)
.backgroundColor(0xF5DEB3)
}.width('90%').height(220).backgroundColor(0xAFEEEE)
List(创建列表)
使用场景为垂直列表比如通讯录,消息列表等展示
在垂直列表中,List按垂直方向自动排列ListItemGroup或ListItem。
ListItemGroup用于列表数据的分组展示,其子组件也是ListItem。ListItem表示单个列表项,可以包含单个子组件。
RelativeContainer(相对布局)
@Entry
@Component
struct Index {
build() {
Row() {
RelativeContainer() {
Row().width(100).height(100)
.backgroundColor("#FF3333")
.alignRules({
top: {anchor: "__container__", align: VerticalAlign.Top},
left: {anchor: "__container__", align: HorizontalAlign.Start}
})
.id("row1")
Row().width(100)
.backgroundColor("#FFCC00")
.alignRules({
top: {anchor: "__container__", align: VerticalAlign.Top},
right: {anchor: "__container__", align: HorizontalAlign.End},
bottom: {anchor: "row1", align: VerticalAlign.Center},
})
.offset({
x:-40,
y:-20
})
.id("row2")
Row().height(100)
.backgroundColor("#FF6633")
.alignRules({
top: {anchor: "row1", align: VerticalAlign.Bottom},
left: {anchor: "row1", align: HorizontalAlign.End},
right: {anchor: "row2", align: HorizontalAlign.Start}
})
.offset({
x:-10,
y:-20
})
.id("row3")
Row()
.backgroundColor("#FF9966")
.alignRules({
top: {anchor: "row3", align: VerticalAlign.Bottom},
bottom: {anchor: "__container__", align: VerticalAlign.Bottom},
left: {anchor: "__container__", align: HorizontalAlign.Start},
right: {anchor: "row1", align: HorizontalAlign.End}
})
.offset({
x:-10,
y:-30
})
.id("row4")
Row()
.backgroundColor("#FF66FF")
.alignRules({
top: {anchor: "row3", align: VerticalAlign.Bottom},
bottom: {anchor: "__container__", align: VerticalAlign.Bottom},
left: {anchor: "row2", align: HorizontalAlign.Start},
right: {anchor: "row2", align: HorizontalAlign.End}
})
.offset({
x:10,
y:20
})
.id("row5")
Row()
.backgroundColor('#ff33ffb5')
.alignRules({
top: {anchor: "row3", align: VerticalAlign.Bottom},
bottom: {anchor: "row4", align: VerticalAlign.Bottom},
left: {anchor: "row3", align: HorizontalAlign.Start},
right: {anchor: "row3", align: HorizontalAlign.End}
})
.offset({
x:-15,
y:10
})
.backgroundImagePosition(Alignment.Bottom)
.backgroundImageSize(ImageSize.Cover)
.id("row6")
}
.width(300).height(300)
.margin({left: 50})
.border({width:2, color: "#6699FF"})
}
.height('100%')
}
}
常用样式
Border
.borderStyle:
BorderStyle: Dashed(虚线)|Dotted(点线)|Solid(实线)
.borderWidth:
left|top|right|bottom: numbe
EdgeStyle:
left|top|right|bottom: any
。。。。后续待补充