ArkUI开发框架是方舟开发框架的简称,它是一套构建 HarmonyOS / OpenHarmony 应用界面的声明式UI开发框架,它使用极简的UI信息语法、丰富的UI组件以及实时界面语言工具,帮助开发者提升应用界面开发效率 30%,开发者只需要使用一套 TS / JS API,就能在多个 HarmonyOS / OpenHarmony 设备上提供既丰富又流畅的用户界面体验。
什么是UI开发框架?
UI:即用户界面,主要包含视觉(比如文字、图像、动画等可以看到的内容)以及交互(比如点击按钮、滑动列表、放缩图片等用户操作)。
UI框架:是为软件应用开发者提供开发UI的基础设施,主要包括UI控件(按钮/列表等)、视图布局(摆放/排列相应的UI控件)、动画机制(动画设计以及效果呈现)、交互事件处理(点击/滑动)等,以及相应的编程语言和编程模型等。
UI编程框架提供了开发以及运行UI界面所需要的框架能力,如下图所示:
说明:
ArkUI开发框架综合考虑了UI渲染、语言和运行效率,围绕着极简开发、高性能、跨设备跨平台进一步演进。下图是ArkUI整体架构的演进:
上图中左侧是 2020 年 HarmonyOS 发布的JS UI框架的架构示意图,主要支持类Web的前端开发范式,通过DSL(domain-specific language,领域特定语言)转换层,跨语言对接到声明式UI后端引擎,并结合JS引擎完成整体UI渲染。右侧是ArkUI开发框架,主要有以下几个变化:
另外,在ArkUI开发框架中,类Web范式会继续保留,即类Web范式和新一代的声明式UI范式都可以支持,可以各自独立使用,但不能混用。
①.极简的UI信息语法
ArkUI开发框架采用基于 TypeScript
扩展的极简的声明式UI描述界面语法,提供了类自然语言的UI描述和组合,开发者只需用几行简单直观的声明式代码,即可完成界面功能。
②.丰富的内置UI组件
ArkUI开发框架内置了丰富而精美的多态组件,可满足大部分应用界面开发的需求,开发者可以轻松地向几乎任何UI控件添加动画并选择一系列框架内置的动画能力,可为用户带来平滑而自然的体验。其中多态是指UI描述是统一的,UI呈现在不同类型设备上会有所不同。比如 Button
组件在手机和手表会有不同的样式和交互方式。
③.多维度的状态管理机制
ArkUI开发框架为开发者提供了跨设备数据绑定功能和多维度的状态管理机制(组件内/组件间/全局/分布式数据驱动UI变更),支持灵活的数据驱动的UI变更,帮助开发者节省70%代码完成跨端界面应用开发。
④.支持多设备开发
ArkUI开发框架除了提供UI开发套件外还围绕着多设备开发提供了多维度的解决方案,进一步简化开发:
⑤.原生性能体验
ArkUI开发框架内置了许多核心的UI控件和动效,如图片、列表、网格、属性动画、转场动画等,加持自研的 ArkCompiler 方舟编译器和 ArkRuntime 方舟运行时深度优化,这些都可以在 HarmonyOS / OpenHarmony 设备上达到移动原生应用一样的性能体验。
⑥.实时预览机制
ArkUI开发框架支持实时界面预览特性可帮助开发快速的所见即所得的开发和调测界面,无需连接真机设备就可以显示应用界面在任何 HarmonyOS / OpenHarmony 设备上的UI效果,预览的关键特性主要包括:
创建项目说明
下面我们以一个具体的示例来说明新一代声明式UI开发范式的基本组成。如下图所示的代码示例,UI界面会显示一个 数字
按钮。当用户点击 +
按钮时,每点击一次会加 1
。
上面的案例构成说明如下:
@Entry
、 @Component
、 @State
都是装饰器。
@Component
表示这是个自定义组件;@Entry
则表示这是个页面入口组件,也就是根组件;@State
表示组件中的状态变量,这个状态变化会引起UI变更。@Component
装饰的 struct Index 。build()
方法内部的代码块。Button
。Button
后面的onClick()
方法。fontSize()
、width()
、height()
、size()
等,可通过链式调用的方式设置多项属性。上述示例中,用 @State
装饰的变量 times
,包含了一个基础的状态管理机制,即 times
的值的变化,会引起相应的UI组件Button的变化,ArkUI开发框架还提供多维度的状态管理机制,和UI相关联的数据,不仅仅在组件内使用,还可以在不同组件层级间传递,比如父子组件之间,爷孙组件之间,也可以是全局范围内的传递,还可以是跨设备传递。
应用开发过程中,经常需要用到颜色、字体、间距、图片等资源,在不同的设备或配置中,这些资源的值可能不同。
应用资源:借助资源文件能力,开发者在应用中自定义资源,自行管理这些资源在不同的设备或配置中的表现。
系统资源:开发者直接使用系统预置的资源定义(即分层参数
,同一资源ID在设备类型、深浅色等不同配置下有不同的取值)。
移动端应用开发常用到的资源比如图片、音视频和字符串等在OpenHarmony 规定把这些应用的资源文件统一放在 resources
目录下的各子目录中便于开发者使用和维护, resoures
目录包括两大类,
base
目录与限定词目录rawfile
目录新建 OpenHarmony 应用,默认生成的资源目录如下所示:
base
目录与限定词目录下面可以创建资源组目录(包括 element
、 media
、 profile
),用于存放特定类型的资源文件,各资源目录说明如下图所示:
OpenHarmony 应用资源分为两类,一类是应用资源,另一类是系统资源,它们的资源访问方式如下:
base 目录下的资源文件会被编译成二进制文件并且给这些资源赋予唯一的 ID ,使用相应资源的时候通过资源访问符 $r(‘app.type.name’) 的形式,
例如:string.json 中新加 name 为 text_string 的字符串,则访问该字符串资源为 $r(‘app.string.text_string’):
在base
目录的子目录element下新建 float.json
,string.json 、color.json
文件,分别存放浮点型、字体和颜色,资源内容如下图所示:
需要注意的是因为国际化的问题:
在media准备一张图片,后面作为文本框的背景图使用:
创建:ResourceDemo.ets文件,应用上面的内容如下:
@Entry
@Component
struct ResourceDemo{
build(){
Column(){
Text($r('app.string.hi_string')) //访问字符串资源
.size({width:300, height:120}) //设置尺寸
.fontSize($r('app.float.text_size'))//访问字体大小
.fontColor($r('app.color.text_color'))//访问字体颜色
.backgroundImage($r('app.media.Sns'), ImageRepeat.XY) //设置背景图片,ImageRepeat.XY则是图片太小时候,选择某个坐标位置的颜色填充
}
.width("100%") //表示将组件的宽度设置为父容器的100%
.height("100%") //表示将组件的高度设置为父容器的100%
.padding(10)//表示设置组件的内边距为10个单位。
}
}
预览效果如下:
除了自定义资源,开发者也可以使用系统中预定义的资源,统一应用的视觉风格。可以查看应用UX设计中关于资源的介绍,获取支持的系统资源ID及其在不同配置下的取值。在开发过程中,分层参数的用法与资源限定词基本一致。对于系统资源,可以通过“$r(‘sys.type.resource_id’)”的形式引用。其中,sys为系统资源;type为资源类型,取值包括“color”、“float”、“string”、“media”;resource_id为资源id。说明
案例,创建 ResourceDemo2.ets内容如下:
@Entry
@Component
struct ResourceDemo2{
build(){
Column(){
Text('HelloWorld')
.fontColor($r('sys.color.ohos_id_color_emphasize')) // 设置字体颜色
.fontSize($r('sys.float.ohos_id_text_size_headline5')) // 设置字体大小
.fontFamily($r('sys.string.ohos_id_text_font_family_medium')) // 设置字体
.backgroundColor($r('sys.color.ohos_id_color_palette_aux1')) // 设置背景颜色
Image($r('sys.media.ohos_app_icon'))
.border({
color: $r('sys.color.ohos_id_color_palette_aux11'), // 设置边框颜色为辅助色11
radius: $r('sys.float.ohos_id_corner_radius_button'), // 设置边框圆角半径为按钮圆角半径
width: 2 // 设置边框宽度为2
})
.margin({
top: $r('sys.float.ohos_id_elements_margin_horizontal_m'), // 设置上边距为水平中等间距
bottom: $r('sys.float.ohos_id_elements_margin_horizontal_l') // 设置下边距为水平大间距
})
.height(200) // 设置高度为200
.width(300) // 设置宽度为300
}
.width("100%")
.height("100%")
.padding(10)
}
}
预览结果如下:
1)像素单位说明
ArkUI开发框架提供了 4 种像素单位供开发者使用,分别是: px 、 vp 、 fp 和 lpx ,框架采用vp为基准数据单位。它们之间的区别如下表所示:
2)像素单位转换
提供其他单位与px单位互相转换的方法。
案例如下:
@Entry
@Component
struct Example {
build() {
Column() {
/**Flex({ wrap: FlexWrap.Wrap })是一个Flex布局的设置,
*其中wrap: FlexWrap.Wrap表示设置Flex容器的子元素在主轴方向上超出容器时是否换行。
*在这里,FlexWrap.Wrap表示子元素会自动换行,以适应容器的尺寸。
* 这样设置可以确保在容器尺寸不足以容纳所有子元素时,子元素会自动换行,而不会超出容器范围。
*/
Flex({ wrap: FlexWrap.Wrap }) {
// 默认不写单位就是是vp
Column() {
Text("width(220)")
.width(220) // 设置宽度为220vp
.height(40) // 设置高度为40vp
.backgroundColor("#00BFC9") // 设置背景颜色为#00BFC9
.fontSize("12vp") // 设置字体大小为12vp
}.margin(5) // 设置外边距为5vp
// 宽度指定成px
Column() {
Text("width('220px')")
.width('220px') // 设置宽度为220px
.height(40) // 设置高度为40vp
.backgroundColor("#007900") // 设置背景颜色为#007900
.textAlign(TextAlign.Center) // 设置文本对齐方式为居中
.fontColor(Color.White) // 设置字体颜色为白色
}.margin(5) // 设置外边距为5vp
// 宽度指定成vp
Column() {
Text("width('220vp')")
.width('220vp') // 设置宽度为220vp
.height(40) // 设置高度为40vp
.backgroundColor("#FF9800") // 设置背景颜色为#FF9800
.textAlign(TextAlign.Center) // 设置文本对齐方式为居中
.fontColor(Color.White) // 设置字体颜色为白色
.fontSize('12vp') // 设置字体大小为12vp
}.margin(5) // 设置外边距为5vp
// 宽度指定成vplpx
Column() {
Text("width('220lpx') designWidth:720")
.width("220lpx") // 设置宽度为220lpx
.height(40) // 设置高度为40vp
.backgroundColor("#634794") // 设置背景颜色为#634794
.textAlign(TextAlign.Center) // 设置文本对齐方式为居中
.fontColor(Color.White) // 设置字体颜色为白色
.fontSize('12vp') // 设置字体大小为12vp
}.margin(5) // 设置外边距为5vp
// 将vp单位的数值转换为以px为单位的数值
Column() {
Text("width(vp2px(220) + 'px')")
.width(vp2px(220) + 'px') // 将220vp转换为px单位的数值,然后设置宽度
.height(40) // 设置高度为40vp
.backgroundColor('#3F56EA') // 设置背景颜色为#3F56EA
.textAlign(TextAlign.Center) // 设置文本对齐方式为居中
.fontColor(Color.White) // 设置字体颜色为白色
.fontSize('12vp') // 设置字体大小为12vp
}.margin(5) // 设置外边距为5vp
// fontSize('12fp')设置成fp
Column() {
Text("fontSize('12fp')")
.width(220) // 设置宽度为220vp
.height(40) // 设置高度为40vp
.backgroundColor('#A14A5C') // 设置背景颜色为#A14A5C
.textAlign(TextAlign.Center) // 设置文本对齐方式为居中
.fontColor(Color.White) // 设置字体颜色为白色
.fontSize('12fp') // 设置字体大小为12fp
}.margin(5) // 设置外边距为5vp
// 将px单位的数值转换为以vp为单位的数值。
Column() {
Text("width(px2vp(220))")
.width(px2vp(220)) // 将220px转换为vp单位的数值,然后设置宽度
.height(40) // 设置高度为40vp
.backgroundColor('#E40078') // 设置背景颜色为#E40078
.textAlign(TextAlign.Center) // 设置文本对齐方式为居中
.fontColor(Color.White) // 设置字体颜色为白色
.fontSize('12fp') // 设置字体大小为12fp
}.margin(5) // 设置外边距为5vp
}.width('100%') // 设置宽度为100%
}
}
}
ArkUI开发框架在 @ohos.resourceManager
模块里提供了资源管理器 ResourceManager
,它可以访问不同的资源,比如获取获取字符串资源,获取设备配置信息等等,参考地址:https://developer.harmonyos.com/cn/docs/documentation/doc-references-V3/js-apis-resource-manager-0000001478181625-V3,resourceManager
模块 API 如下:
declare namespace resourceManager {
// 省略部分代码
export interface ResourceManager {
// 获取字符串资源
getString(resId: number, callback: AsyncCallback): void;
// 获取字符串数组资源
getStringArray(resId: number, callback: AsyncCallback>): void;
// 获取媒体资源
getMedia(resId: number, callback: AsyncCallback): void;
// 获取设备信息,比如当前屏幕密度,设备类型是手机还是平板等
getDeviceCapability(callback: AsyncCallback): void;
// 获取配置信息,比如当前屏幕方向密度,当前设备语言
getConfiguration(callback: AsyncCallback): void;
// 释放ResourceManager资源
release();
}
}
export default resourceManager;
使用 ResourceManager
之前先调用 getContext(this)
方法获取当前组件的 Context,该 Conetxt 内部定义了一个 ResourceManager
的属性,因此可以直接使用 ResourceManager
的各种 getXXX()
方法获取对应资源, ResourceManager应用
流程如下:
1)引入 resourceManager
import resourceManager from '@ohos.resourceManager';
2)获取 ResourceManager
aboutToAppear() {
// 获取ResourceManager
let manager = getContext(this).resourceManager;
}
3)使用 ResourceManager
manager.getString(0x1000001, (innerError, data) => {
if(data) {
// 获取资源成功
} else {
console.log("error: " + JSON.stringify(innerError))
}
})
4)释放 ResourceManager
this.manager.release();
完整案例如下:
import resourceManager from '@ohos.resourceManager'; // 导入资源管理器模块
@Entry
@Component
struct Example02{
@State text_string: string = ""; // 定义文本字符串状态
@State capability: string = ""; // 定义设备能力状态
@State configuration: string = ""; // 定义设备配置状态
private resManager: resourceManager.ResourceManager; // 声明资源管理器
build(){
Column({space:10}){ // 创建一个垂直布局的Column组件,设置子元素间距为10
Text(this.text_string) // 显示文本字符串资源
.size({width:300, height:120}) // 设置尺寸为宽300,高120
.fontSize($r('app.float.text_size')) // 使用资源管理器获取字体大小
.fontColor($r('app.color.text_color')) // 使用资源管理器获取字体颜色
.backgroundImage($r('app.media.Sns')) // 使用资源管理器设置背景图片
Text(this.capability) // 显示设备能力信息
.fontSize(20) // 设置字体大小为20
Text(this.configuration) // 显示设备配置信息
.fontSize(20) // 设置字体大小为20
}.width('100%') // 设置宽度为100%
.height('100%') // 设置高度为100%
.padding(10) // 设置内边距为10
}
aboutToAppear(){
this.resManager = getContext(this).resourceManager; // 获取上下文中的资源管理器
this.resManager.getStringValue(0x1000001,(innerError,data)=>{ // 获取字符串资源值
if(data){
this.text_string = data; // 将获取到的字符串资源值赋给text_string状态
}else{
console.log("error:"+JSON.stringify(innerError)); // 打印错误信息
}
this.resManager.getDeviceCapability((innerError, deviceCapability)=>{ // 获取设备能力信息
if(deviceCapability){
this.capability = JSON.stringify(deviceCapability); // 将设备能力信息转换为JSON字符串并赋给capability状态
}
})
this.resManager.getConfiguration((innerError, configuration)=>{ // 获取设备配置信息
if(configuration){
this.configuration = JSON.stringify(configuration); // 将设备配置信息转换为JSON字符串并赋给configuration状态
}
})
})
}
aboutToDisappear(){
this.resManager?.release(); // 释放资源管理器资源
}
}
注意:渲染出来的 mock string 是因为在预览器上暂时不支持 ResourceManager 的用法,在实际设备上可以执行。
ArkTS也提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的UI内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。参考网址:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ets-rendering-control-0000001149698611
3.1.if/else条件渲染
使用 if/else 进行条件渲染需要注意以下情况:
案例如下:
@Entry
@Component
struct ComponentTest {
@State showImage: boolean = false; // 定义showImage状态,默认为false
build() {
// 纵向布局元素垂直方向间距。
Column({space:10}){ // 创建一个垂直布局的Column组件,设置子元素间距为10
// 如果showImage为true,则显示图片,否则显示"Loading ....."
if(this.showImage){
Image($r('app.media.Sns')) // 显示图片,使用资源管理器获取图片资源
.width(160) // 设置宽度为160
.height(60) // 设置高度为60
.backgroundColor(Color.Pink) // 设置背景颜色为粉色
}else {
Text("Loading .....")
.fontSize(23) // 设置字体大小为23
.fontColor('#FFFFF0') //设置字体颜色为象牙色
.width(160) // 设置宽度为160
.height(60) // 设置高度为60
.backgroundColor(Color.Pink) // 设置背景颜色为粉色
.textAlign(TextAlign.Center) // 设置文本居中对齐
}
Button(this.showImage?'Image Loaded':'Load Image') // 按钮内容根据showImage状态动态显示
.size({width:160,height:40}) // 设置按钮的宽和高
.backgroundColor(this.showImage?'#3F56EA':'#9C554B') // 根据showImage状态设置按钮背景色
.onClick(()=>{ // 点击事件处理
this.showImage = true; // 点击后设置showImage值为true,显示图片
})
}.width('100%') // 设置宽度为100%
.height('100%') // 设置高度为100%
.padding(10) // 设置内边距为10
}
}
预览效果如下:
ArkUI开发框架提供循环渲染(ForEach组件)来迭代数组,并为每个数组项创建相应的组件。ForEach
定义如下:
ArkUI开发框架提供循环渲染(ForEach组件)来迭代数组,并为每个数组项创建相应的组件。ForEach
定义如下:
interface ForEach {(
arr: Array,
itemGenerator: (item: any, index?: number) => void,
keyGenerator?: (item: any, index?: number) => string
): ForEach;
}
说明:
案例如下:
@Entry
@Component
struct ComponentTest02 {
//准备源数据
private textArray:string[] = ["一","二","三","四","五"] // 定义一个包含字符串的数组作为源数据
build() {
Column({space:10}){ // 创建一个垂直布局的Column组件,设置子元素间距为10
ForEach(this.textArray,(item:string,index?:number)=>{ // 遍历textArray数组
Text(`标题:${item}`) // 显示带有标题前缀的文本
.fontSize(20) // 设置字体大小为20
.backgroundColor('#00B377') // 设置背景颜色为绿色
.margin({top:10}) // 设置上边距为10
.fontColor('#FFFFF0') // 设置字体颜色为白色
})
}.width("100%") // 设置宽度为100%
.height("100%") // 设置高度为100%
.padding(10) // 设置内边距为10
}
}
预览后效果如下:
ArkUI开发框架通过数据懒加载(LazyForEach)从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。
1)LazyForEach 定义如下:
// LazyForEach定义
interface LazyForEach {(dataSource: IDataSource, itemGenerator: (item: any, index?: number) => void,keyGenerator?: (item: any, index?: number) => string): LazyForEach;
}
// IDataSource定义
export declare interface IDataSource {
totalCount(): number;
getData(index: number): any;
registerDataChangeListener(listener: DataChangeListener): void;
unregisterDataChangeListener(listener: DataChangeListener): void;
}
// DataChangeListener定义
export declare interface DataChangeListener {
onDataReloaded(): void;
onDataAdded(index: number): void;
onDataMoved(from: number, to: number): void;
onDataDeleted(index:number): void;
onDataChanged(index:number): void;
}
说明如下:
2)IDataSource 定义如下:
export declare interface IDataSource {
totalCount(): number;
getData(index: number): any;
registerDataChangeListener(listener: DataChangeListener): void;
unregisterDataChangeListener(listener: DataChangeListener): void;
}
说明如下:
3)DataChangeListener 定义如下:
export declare interface DataChangeListener {
onDataReloaded(): void;
onDataAdded(index: number): void;
onDataMoved(from: number, to: number): void;
onDataDeleted(index:number): void;
onDataChanged(index:number): void;
}
说明如下:
案例如下:
//##########################构造数据################################
//定义student
class Student{
public sid: number; // 学生ID
public name: string; // 学生姓名
public age: number; // 学生年龄
public address: string; // 学生地址
public avatar: string; // 学生头像
//构造方法
constructor(sid: number, name: string, age: number, address: string, avatar: string) {
this.sid = sid; // 初始化学生ID
this.name = name; // 初始化学生姓名
this.age = age; // 初始化学生年龄
this.address = address; // 初始化学生地址
this.avatar = avatar; // 初始化学生头像
}
}
// 定义一个抽象类 BaseDataSource,使用泛型 T,实现接口 IDataSource
abstract class BaseDataSource implements IDataSource {
private dataSource: T[] = new Array(); // 数据源,使用泛型数组
// 构造方法,接收一个泛型数组作为参数,用于初始化数据源
constructor(dataList: T[]) {
this.dataSource = dataList; // 初始化数据源
}
// 返回数据源的长度
totalCount(): number {
return this.dataSource == null ? 0 : this.dataSource.length;
}
// 获取指定索引的数据,如果索引合法则返回数据,否则返回 null
getData(index: number): T | null {
return index >= 0 && index < this.totalCount() ? this.dataSource[index] : null;
}
// 注册数据变化监听器,这里的方法体为空,需要在子类中实现具体逻辑
registerDataChangeListener(listener: DataChangeListener) {}
// 取消注册数据监听器,这里的方法体为空,需要在子类中实现具体逻辑
unregisterDataChangeListener() {}
}
// 继承 BaseDataSource 类,指定泛型为 Student 类型
class StudentDataSource extends BaseDataSource {
constructor(students: Student[]) {
super(students); // 调用父类构造函数进行初始化
}
}
//产生数据
function mock():Student[]{
let students = [];
for (let i = 1; i < 21; i++) {
//模拟学生数据
students[i] = new Student(i,"student:"+i, i+10, "address:"+i, "app.media.Sns");
}
return students;
}
@Entry
@Component
struct ComponentTest03 {
// mock数据
private student: Student[] = mock(); // 模拟学生数据
// 创建dataSource
private dataSource: StudentDataSource = new StudentDataSource(this.student); // 创建学生数据源
build() {
Column({ space: 10 }) { // 创建一个垂直布局的列,设置间距为10
List() { // 创建一个列表
LazyForEach(this.dataSource, (item: Student) => { // 使用自定义dataSource进行懒加载
ListItem() { // 创建列表项
Row() { // 创建一个水平布局的行
Image($r("app.media.Sns")) // 显示学生头像
.height('100%') // 设置高度为100%
.width(80) // 设置宽度为80
Column() { // 创建一个垂直布局的列
Text(this.getName(item)) // 调用getName方法验证懒加载
.fontSize(20) // 设置字体大小为20
Text('address: ' + item.address) // 显示学生地址
.fontSize(17) // 设置字体大小为17
}
.margin({ left: 5 }) // 设置左边距为5
.alignItems(HorizontalAlign.Start) // 设置子元素水平方向对齐方式为起始位置
.layoutWeight(1) // 设置布局权重为1
}
.width('100%') // 设置宽度为100%
.height('100%') // 设置高度为100%
}
.width('100%') // 设置宽度为100%
.height(60) // 设置高度为60
})
}
.divider({ // 设置列表的分隔线样式
strokeWidth: 3, // 分隔线宽度为3
color: Color.Gray // 分隔线颜色为灰色
})
.width('90%') // 设置宽度为90%
.height(160) // 设置高度为160
.backgroundColor(Color.Pink) // 设置背景颜色为粉色
}
.width('100%') // 设置宽度为100%
.height('100%') // 设置高度为100%
.padding(10) // 设置内边距为10
}
getName(item: Student): string {
console.log("index: " + item.sid); // 打印学生索引日志
return 'index:' + item.sid + ", " + item.name; // 返回学生索引和姓名
}
}
执行后效果如下:
输出的日志如下:
说明:
LazyForEach(dataSource,
item => Text(`${item.i}. item.data.label`)),
item => item.data.id.toString())
为了能让大家更好的学习鸿蒙 (OpenHarmony) 开发技术,这边特意整理了《鸿蒙 (OpenHarmony)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05
https://qr21.cn/FV7h05
https://qr21.cn/FV7h05
https://qr21.cn/FV7h05
https://qr21.cn/FV7h05
https://qr21.cn/FV7h05
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……