初识ArkUI
ArkUI简介
ArkUI(方舟UI框架)为应用的UI开发提供了完整的基础设施,包括简洁的UI语法、丰富的UI功能(组件、布局、动画以及交互事件),以及实时界面预览工具等,可以支持开发者进行可视化界面开发。
基本概念
-
UI: 即用户界面。开发者可以将应用的用户界面设计为多个功能页面,每个页面进行单独的文件管理,并通过页面路由API完成页面间的调度管理如跳转、回退等操作,以实现应用内的功能解耦。
-
组件: UI构建与显示的最小单位,如列表、网格、按钮、单选框、进度条、文本等。开发者通过多种组件的组合,构建出满足自身应用诉求的完整界面。
两种开发范式
-
针对不同的应用场景及技术背景,方舟UI框架提供了两种开发范式,分别是基于ArkTS的声明式开发范式(简称“声明式开发范式”)和兼容JS的类Web开发范式(简称“类Web开发范式”)。
-
声明式开发范式:采用基于TypeScript声明式UI语法扩展而来的ArkTS语言,从组件、动画和状态管理三个维度提供UI绘制能力。
-
类Web开发范式:采用经典的HML、CSS、JavaScript三段式开发方式,即使用HML标签文件搭建布局、使用CSS文件描述样式、使用JavaScript文件处理逻辑。该范式更符合于Web前端开发者的使用习惯,便于快速将已有的Web应用改造成方舟UI框架应用
-
在开发一款新应用时,推荐采用声明式开发范式来构建UI,主要基于以下几点考虑:
-
开发效率:
声明式开发范式更接近自然语义的编程方式,开发者可以直观地描述UI,无需关心如何实现UI绘制和渲染,开发高效简洁。
- 应用性能:
如下图所示,两种开发范式的UI后端引擎和语言运行时是共用的,但是相比类Web开发范式,声明式开发范式无需JS框架进行页面DOM管理,渲染更新链路更为精简,占用内存更少,应用性能更佳。
- 发展趋势:
声明式开发范式后续会作为主推的开发范式持续演进,为开发者提供更丰富、更强大的能力。
- 不同应用类型支持的开发范式
- 根据所选用应用模型(Stage模型、FA模型)和页面形态(应用或服务的普通页面、卡片)的不同,对应支持的UI开发范式也有所差异,详见下表。
应用模型 | 页面形态 | 支持的UI开发范式 |
---|---|---|
Stage模型(推荐) | 应用或服务的页面 | 声明式开发范式(推荐) |
卡片 | 声明式开发范式(推荐)类Web开发范式 | |
FA模型 | 应用或服务的页面 | 声明式开发范式类Web开发范式 |
卡片 | 类Web开发范式 |
ArkTS声明式开发范式
- 基于ArkTS的声明式开发范式的方舟开发框架是一套开发极简、高性能、支持跨设备的UI开发框架,提供了构建应用UI所必需的能力
ArkTS
- ArkTS是优选的主力应用开发语言,围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展。扩展能力包含声明式UI描述、自定义组件、动态扩展UI元素、状态管理和渲染控制。状态管理作为基于ArkTS的声明式开发范式的特色,通过功能不同的装饰器给开发者提供了清晰的页面更新渲染流程和管道。状态管理包括UI组件状态和应用程序状态,两者协作可以使开发者完整地构建整个应用的数据更新和UI渲染。ArkTS语言的基础知识请参考初识ArkTS语言。
布局
- 布局是UI的必要元素,它定义了组件在界面中的位置。ArkUI框架提供了多种布局方式,除了基础的线性布局、层叠布局、弹性布局、相对布局、栅格布局外,也提供了相对复杂的列表、宫格、轮播。
组件
- 组件是UI的必要元素,形成了在界面中的样子,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。系统内置组件包括按钮、单选框、进度条、文本等。开发者可以通过链式调用的方式设置系统内置组件的渲染效果。开发者可以将系统内置组件组合为自定义组件,通过这种方式将页面组件化为一个个独立的UI单元,实现页面不同单元的独立创建、开发和复用,具有更强的工程性。页面路由和组件导航
页面路由和组件导航
- 应用可能包含多个页面,可通过页面路由实现页面间的跳转。一个页面内可能存在组件间的导航如典型的分栏,可通过导航组件实现组件间的导航。
图形
- 方舟开发框架提供了多种类型图片的显示能力和多种自定义绘制的能力,以满足开发者的自定义绘图需求,支持绘制形状、填充颜色、绘制文本、变形与裁剪、嵌入图片等。
动画
- 动画是UI的重要元素之一。优秀的动画设计能够极大地提升用户体验,框架提供了丰富的动画能力,除了组件内置动画效果外,还包括属性动画、显式动画、自定义转场动画以及动画API等,开发者可以通过封装的物理模型或者调用动画能力API来实现自定义动画轨迹。
交互事件
- 交互事件是UI和用户交互的必要元素。方舟开发框架提供了多种交互事件,除了触摸事件、鼠标事件、键盘按键事件、焦点事件等通用事件外,还包括基于通用事件进行进一步识别的手势事件。手势事件有单一手势如点击手势、长按手势、拖动手势、捏合手势、旋转手势、滑动手势,以及通过单一手势事件进行组合的组合手势事件。
自定义能力
- 自定义能力是UI开发框架提供给开发者对UI界面进行开发和定制化的能力。包括:自定组合、自定义扩展、自定义节点和自定义渲染。
特点
开发效率高,开发体验好
- 代码简洁:通过接近自然语义的方式描述UI,不必关心框架如何实现UI绘制和渲染。
- 数据驱动UI变化:让开发者更专注自身业务逻辑的处理。当UI发生变化时,开发者无需编写在不同的UI之间进行切换的UI代码, 开发人员仅需要编写引起界面变化的数据,具体UI如何变化交给框架。
开发体验好:界面也是代码,让开发者的编程体验得到提升。
性能优越
- 声明式UI前端和UI后端分层:UI后端采用C++语言构建,提供对应前端的基础组件、布局、动效、交互事件、组件状态管理和渲染管线。
- 语言编译器和运行时的优化:统一字节码、高效FFI-Foreign Function Interface、AOT-Ahead Of Time、引擎极小化、类型优化等。
- 生态容易快速推进
- 能够借力主流语言生态快速推进,语言相对中立友好,有相应的标准组织可以逐步演进
整体架构
- 声明式UI前端
- 提供了UI开发范式的基础语言规范,并提供内置的UI组件、布局和动画,提供了多种状态管理机制,为应用开发者提供一系列接口支持。
- 语言运行时
- 选用方舟语言运行时,提供了针对UI范式语法的解析能力、跨语言调用支持的能力和TS语言高性能运行环境。
- 声明式UI后端引擎
- 后端引擎提供了兼容不同开发范式的UI渲染管线,提供多种基础组件、布局计算、动效、交互事件,提供了状态管理和绘制能力。
- 渲染引擎
- 提供了高效的绘制能力,将渲染管线收集的渲染指令,绘制到屏幕的能力。
- 平台适配层
- 提供了对系统平台的抽象接口,具备接入不同系统的能力,如系统渲染管线、生命周期调度等。
- 提供了对系统平台的抽象接口,具备接入不同系统的能力,如系统渲染管线、生命周期调度等。
布局概述
-
组件按照布局的要求依次排列,构成应用的页面。在声明式UI中,所有的页面都是由自定义组件构成,开发者可以根据自己的需求,选择合适的布局进行页面开发。
-
常用布局
-
为实现上述效果,开发者需要在页面中声明对应的元素。其中,Page表示页面的根节点,Column/Row等元素为系统组件。针对不同的页面结构,ArkUI提供了不同的布局组件来帮助开发者实现对应布局的效果,例如Row用于实现线性布局。
布局元素的组成
-
布局相关的容器组件可形成对应的布局效果
-
组件区域(蓝区方块):
组件区域表示组件的大小,width、height属性用于设置组件区域的大小。
- 组件内容区(黄色方块):
组件内容区大小为组件区域大小减去组件的border值,组件内容区大小会作为组件内容(或者子组件)进行大小测算时的布局测算限制。
- 组件内容(绿色方块):
组件内容本身占用的大小,比如文本内容占用的大小。组件内容和组件内容区不一定匹配,比如设置了固定的width和height,此时组件内容的大小就是设置的width和height减去padding和border值,但文本内容则是通过文本布局引擎测算后得到的大小,可能出现文本真实大小小于设置的组件内容区大小。当组件内容和组件内容区大小不一致时,align属性生效,定义组件内容在组件内容区的对齐方式,如居中对齐。
- 组件布局边界(虚线部分):
组件通过margin属性设置外边距时,组件布局边界就是组件区域加上margin的大小。
线性布局
- 线性布局(LinearLayout)是开发中最常用的布局,通过线性容器Row和Column构建。线性布局是其他布局的基础,其子元素在线性方向上(水平方向和垂直方向)依次排列。
- 线性布局的排列方向由所选容器组件决定,Column容器内子元素按照垂直方向排列,Row容器内子元素按照水平方向排列。
基本概念
- 布局容器:具有布局能力的容器组件,可以承载其他元素作为其子元素,布局容器会对其子元素进行尺寸计算和布局排列。
- 布局子元素:布局容器内部的元素。
- 主轴:线性布局容器在布局方向上的轴线,子元素默认沿主轴排列。
- Row容器主轴为水平方向,Column容器主轴为垂直方向。
- 交叉轴:垂直于主轴方向的轴线。
- Row容器交叉轴为垂直方向,Column容器交叉轴为水平方向。
- 间距:布局子元素的间距。
布局子元素在排列方向上的间距
在布局容器内,可以通过space属性设置排列方向上子元素的间距,使各子元素在排列方向上有等间距效果
参数名 | 参数类型 | 参数描述 |
---|---|---|
space | number | string | 横向布局元素间距。从API version 9开始,space为负数或者justifyContent设置为FlexAlign.SpaceBetween、FlexAlign.SpaceAround、FlexAlign.SpaceEvenly时不生效。默认值:0,单位vp**说明:**可选值为大于等于0的数字,或者可以转换为数字的字符串。 |
Row
- 沿水平方向布局容器
- 属性:
属性名 | 类型 | 必填 | 说明 |
---|---|---|---|
justifyContent8+ | FlexAlign | 是 | 子组件在水平方向上的对齐格式。默认值:FlexAlign.Start |
alignItems | VerticalAlign | 是 | 子组件在垂直方向上的对齐格式。默认值:VerticalAlign.Center |
3.实例
Column
沿垂直方向布局的容器。
属性 | 类型 | 必填 | 说明 |
---|---|---|---|
alignItems | HorizontalAlign | 是 | 子组件在水平方向上的对齐格式。默认值:HorizontalAlign.Center |
justifyContent8+ | FlexAlign | 是 | 子组件在垂直方向上的对齐格式。默认值:FlexAlign.Start |
实例
自适应拉伸
在线性布局下,常用空白填充组件Blank,在容器主轴方向自动填充空白空间,达到自适应拉伸效果。Row和Column作为容器,只需要添加宽高为百分比,当屏幕宽高发生变化时,会产生自适应效果
自适应缩放
- 自适应缩放是指子元素随容器尺寸的变化而按照预设的比例自动调整尺寸,适应各种不同大小的设备。在线性布局中,可以使用以下两种方法实现自适应缩放。
- 父容器尺寸确定时,使用layoutWeight属性设置子元素和兄弟元素在主轴上的权重,忽略元素本身尺寸设置,使它们在任意尺寸的设备下自适应占满剩余空间。
- 父容器尺寸确定时,使用百分比设置子元素和兄弟元素的宽度,使他们在任意尺寸的设备下保持固定的自适应占比。
自适应延伸
- 自适应延伸是指在不同尺寸设备下,当页面的内容超出屏幕大小而无法完全显示时,可以通过滚动条进行拖动展示。这种方法适用于线性布局中内容无法一屏展示的场景。通常有以下两种实现方式。
- 在List中添加滚动条:当List子项过多一屏放不下时,可以将每一项子元素放置在不同的组件中,通过滚动条进行拖动展示。可以通过scrollBar属性设置滚动条的常驻状态,edgeEffect属性设置拖动到内容最末端的回弹效果。
- 使用Scroll组件:在线性布局中,开发者可以进行垂直方向或者水平方向的布局。当一屏无法完全显示时,可以在Column或Row组件的外层包裹一个可滚动的容器组件Scroll来实现可滑动的线性布局。
- 垂直方向布局中使用Scroll组件:
- 水平方向布局中使用Scroll组件:
@Entry
@Component
struct ColPage {
@State message: string = '线性布局';
build() {
Column({space:10}) {
Row({space:20}){
Column().width(40).height(40).backgroundColor('gray')
Column().width(40).height(40).backgroundColor('gray')
Column().width(40).height(40).backgroundColor('gray')
Column().width(40).height(40).backgroundColor('gray')
}
.width('90%')
.height(60)
.border({color:Color.Blue,width:1,style:BorderStyle.Solid})
.layoutWeight(2)
Row(){
Column().width(40).height(40).backgroundColor('red').layoutWeight(1)
Column().width(40).height(40).backgroundColor('black').layoutWeight(1)
Column().width(40).height(40).backgroundColor('green').layoutWeight(1)
Column().width(40).height(40).backgroundColor('blue').layoutWeight(1)
}
.width('90%')
.height(60)
.border({color:Color.Blue,width:1,style:BorderStyle.Solid})
.justifyContent(FlexAlign.SpaceBetween)
.layoutWeight(1)
Row(){
Column().width(40).height(40).backgroundColor('gray')
Column().width(40).height(40).backgroundColor('gray')
Column().width(40).height(40).backgroundColor('gray')
Column().width(40).height(40).backgroundColor('gray')
}
.width('90%')
.height(60)
.border({color:Color.Blue,width:1,style:BorderStyle.Solid})
.justifyContent(FlexAlign.SpaceEvenly)//水平对齐
.alignItems(VerticalAlign.Top)//垂直对齐
Row(){
Text('你好')
//填充空白
Blank().color('red')
//组件提供勾选框样式、状态按钮样式及开关样式.
Toggle({type:ToggleType.Switch,isOn:true})
}
.width('90%')
.height(60)
.border({color:Color.Blue,width:1,style:BorderStyle.Solid})
Row(){
Column().width(40).height(40).backgroundColor('gray')
Column().width(40).height(40).backgroundColor('gray')
Column().width(40).height(40).backgroundColor('gray')
Column().width(40).height(40).backgroundColor('gray')
}
.width('90%')
.height(60)
.border({color:Color.Blue,width:1,style:BorderStyle.Solid})
}
.height('100%')
.width('100%')
.border({color:Color.Red,width:1,style:BorderStyle.Dotted})
.justifyContent(FlexAlign.Start)///主轴对齐方式
.alignItems(HorizontalAlign.Start)//侧轴对齐
}
}
@Entry
@Component
struct ScrollPage {
@State message: string = 'Hello World';
@State nums:number[]=[1,2,3,4,5,6,7,8,9,10]
build() {
Scroll() {
Column({space:10}){
ForEach(this.nums,(n:number,i)=>{
Row(){
Text(n.toString())
}
.height(100)
.width('90%')
.backgroundColor('gray')
.justifyContent(FlexAlign.Center)
.borderRadius(10)
})
}
.width('100%')
}
.height('100%')
.width('100%')
.scrollable(ScrollDirection.Vertical)
.scrollBar(BarState.Auto)//开关滚动条
.scrollBarColor('red')//滚动条颜色
.scrollBarWidth(2)
}
}
.backgroundColor('gray')
.justifyContent(FlexAlign.Center)
.borderRadius(10)
})
}
.width('100%')
}
.height('100%')
.width('100%')
.scrollable(ScrollDirection.Vertical)
.scrollBar(BarState.Auto)//开关滚动条
.scrollBarColor('red')//滚动条颜色
.scrollBarWidth(2)
}
}