Harmony ArkTS语言(上)

简介: Harmony ArkTS语言(上)

  随着华为宣布鸿蒙后续的版本不再兼容Android应用之后,对于现在的开发环境来说有一些冲击,一部分人想做鸿蒙应用开发,另一部分人觉得鸿蒙现在就想替换安卓还为之尚早,不管怎么说,学习是没有错的,哪怕是作为知识储备也是好的,今天就简单说一下鸿蒙应用开发支持的主流语言 ArkTS


前言


  说到ArkTS就得说一下DevEco Studio的演变过程,在我写一篇关于鸿蒙的文章时,DevEco Studio才刚推出不久,当时所支持的语言是Java、JS、C++等,在后续的版本中逐渐去掉了Java,C++,最终使用到了ArkTS,那么我们下面来了解一下ArkTS的由来。


正文


  ArkTS是HarmonyOS主力应用开发语言,它在TypeScript(简称TS)的基础上,匹配ArkUI框架,扩展了声明式UI、状态管理等相应的能力,让开发者以更简洁、更自然的方式开发跨端应用,如果你之前接触过FlutterDartKotlinCompose,那么你对于这个ArkTS的使用应该问题不大。

  ArkTS的构成如下图所示

59b30f4756ce4493b7bad8faa854e9fa.png

JavaScriptTypeScriptArkTS的关系:

  • JavaScript是一种属于网络的高级脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。
  • TypeScriptJavaScript的一个超集,它扩展了JavaScript的语法,通过在JavaScript的基础上添加静态类型定义构建而成,是一个开源的编程语言。
  • ArkTS基于TypeScript语言,拓展了声明式UI、状态管理、并发任务等能力。


一、声明式UI

  声明式UI有两个特征分别是:声明式描述状态驱动视图更新,那么怎么体现这一点呢?我们结合代码来说明,首先打开DevEco Studio,这里我使用的版本是DevEco Studio 3.1.1 Release,创建工程。

ac1cc89d6e1249dc8b283152f217a2c6.png

点击Next。

9139dd3ee65a4c78bfcbf03203155382.png

输入项目名称和包名,这里会说明使用的方式和语言及SDK版本和使用的设备类型,修改好之后点击Finish,完成项目的创建。

84d0a6079b0342b2904fca683c93c8c9.png

完成创建之后我们就可以看到index.ets的代码如图所示,下面我们对这段代码进行一个解析:

  • 装饰器,用来装饰类、结构体、方法以及变量,赋予其特殊的含义,如上述示例中 @Entry@Component@State 都是装饰器。具体而言, @Component 表示这是个自定义组件; @Entry 则表示这是个入口组件; @State 表示组件中的状态变量,此状态变化会引起 UI 变更。
  • 自定义组件,可复用的 UI 单元,可组合其它组件,如上述被 @Component 装饰的 struct Index
  • UI 描述,声明式的方式来描述 UI 的结构,如上述 build() 方法内部的代码块。
  • 内置组件,框架中默认内置的基础和布局组件,可直接被开发者调用,比如示例中的 Row、Column、Text
  • 事件方法,用于添加组件对事件的响应逻辑,统一通过事件方法进行设置,比如为组件添加onClick()
  • 属性方法,用于组件属性的配置,统一通过属性方法进行设置,如fontSize()、width()、height()、color() 等,可通过链式调用的方式设置多项属性。

下面我们预览看一下,点击右侧边栏的Previewer,等待一小会。

94364ed3cd024a27a6218dd95b3b9355.png

下面我们修改index.ets中的代码,然后Ctrl + S保存一下,右侧的预览画面就会更新。

6feb57f5bc3b4c1d8238ef0f66931ac1.png

预览更新后

5b72b3b8e7db4bc38902fe33d73115c6.png

点击Test按钮之后

82f2ff24a91448c3b17fac77c954b752.png

  这里我们添加了一个按钮,同时添加了点击事件,事件中修改了message的值,而message是由@State修饰的,那么就会出发UI刷新,刷新后,Text组件所显示的内容就会从Hello World变成Hello ArkTS,这就是声明式UI的另一特征:状态驱动视图更新


二、数据列表


  上面的示例比较简单,下面我们做一个有点难度的示例,该示例源于鸿蒙学堂官网,感兴趣的可以去学习。

image.gif

① 创建ArkTS文件

  首先我们在ets目录下创建一个model目录,model目录下创建一个RankData.ets文件,代码如下所示:

export class RankData {
  id: number | string
  name: Resource
  vote: string
  constructor(id: number | string, name: Resource, vote: string) {
    this.id = id;
    this.name = name;
    this.vote = vote;
  }
}


  这里的代码简单说明一下,export 表示可以在其他模块中使用,这里的含义就在于我们将RankData反正model目录下,如果我们pages下要使用这个RankData,则RankData本身就需要支持调用才行,因此需要export进行修饰,调用的地方则使用import作为插入。


而id属性的定义是一个联合类型,这属于TypeScript的基础数据类型,表示取值可以为多种类型中的一种。number表示数字,string就是字符串,Resource就表示资源,比如string.app_name这种方式。构造函数就没有什么好说的了,就是属性赋值而已。


② 添加资源

  我们看到和ets目录平级的是resources,该目录下毫无疑问就是资源目录,目录下有三个文件夹,base属于基础资源目录里面可以放置文字、颜色、音频、配置文件等,en_US就是英文下的文字资源,zh_CN就是中文下的文字资源,三个目录下的文字资源文件都是json格式的,下面我们修改base/element/string.jsonen_US/element/string.json中的代码:

{
  "string": [
    {
      "name": "module_desc",
      "value": "module description"
    },
    {
      "name": "EntryAbility_desc",
      "value": "description"
    },
    {
      "name": "EntryAbility_label",
      "value": "label"
    },
    {
      "name": "page_type",
      "value": "variety"
    },
    {
      "name": "page_number",
      "value": "ranking"
    },
    {
      "name": "page_vote",
      "value": "vote"
    },
    {
      "name": "prompt_text",
      "value": "Press again to exit the app"
    },
    {
      "name": "title",
      "value": "Ranking List"
    },
    {
      "name": "title_default",
      "value": " "
    },
    {
      "name": "fruit_watermelon",
      "value": "watermelon"
    },
    {
      "name": "fruit_apple",
      "value": "apple"
    },
    {
      "name": "fruit_banana",
      "value": "banana"
    },
    {
      "name": "fruit_grapes",
      "value": "grapes"
    },
    {
      "name": "fruit_red_grape",
      "value": "grape"
    },
    {
      "name": "fruit_pears",
      "value": "pears"
    },
    {
      "name": "fruit_pineapple",
      "value": "pineapple"
    },
    {
      "name": "fruit_durian",
      "value": "durian"
    },
    {
      "name": "fruit_guava",
      "value": "guava"
    },
    {
      "name": "fruit_carambola",
      "value": "carambola"
    }
  ]
}


再修改zh_CN/element/string.json中的代码:

{
  "string": [
    {
      "name": "module_desc",
      "value": "模块描述"
    },
    {
      "name": "EntryAbility_desc",
      "value": "description"
    },
    {
      "name": "EntryAbility_label",
      "value": "label"
    },
    {
      "name": "page_type",
      "value": "种类"
    },
    {
      "name": "page_number",
      "value": "排名"
    },
    {
      "name": "page_vote",
      "value": "得票数"
    },
    {
      "name": "prompt_text",
      "value": "再按一次退出程序"
    },
    {
      "name": "title",
      "value": "排行榜"
    },
    {
      "name": "title_default",
      "value": " "
    },
    {
      "name": "fruit_watermelon",
      "value": "西瓜"
    },
    {
      "name": "fruit_apple",
      "value": "苹果"
    },
    {
      "name": "fruit_banana",
      "value": "香蕉"
    },
    {
      "name": "fruit_grapes",
      "value": "葡萄"
    },
    {
      "name": "fruit_red_grape",
      "value": "红提"
    },
    {
      "name": "fruit_pears",
      "value": "梨子"
    },
    {
      "name": "fruit_pineapple",
      "value": "菠萝"
    },
    {
      "name": "fruit_durian",
      "value": "榴莲"
    },
    {
      "name": "fruit_guava",
      "value": "番石榴"
    },
    {
      "name": "fruit_carambola",
      "value": "杨桃"
    }
  ]
}


下面我们制造一些假数据,在model包下新建一个DataModel.ets文件,代码如下所示:

import { RankData } from './RankData'
export {rankData1, rankData2}
const rankData1: RankData[] = [
  new RankData(1, $r('app.string.fruit_apple'), "10000"),
  new RankData(2, $r('app.string.fruit_grapes'), '10320'),
  new RankData(3, $r('app.string.fruit_watermelon'), '9801'),
  new RankData(4, $r('app.string.fruit_banana'), '8431'),
  new RankData(5, $r('app.string.fruit_pineapple'), '7546'),
  new RankData(6, $r('app.string.fruit_durian'), '7431'),
  new RankData(7, $r('app.string.fruit_red_grape'), '7187'),
  new RankData(8, $r('app.string.fruit_pears'), '7003'),
  new RankData(9, $r('app.string.fruit_carambola'), '6794'),
  new RankData(10, $r('app.string.fruit_guava'), '6721')
]
const rankData2: RankData[] = [
  new RankData('11', $r('app.string.fruit_watermelon'), '8836'),
  new RankData('12', $r('app.string.fruit_apple'), '8521'),
  new RankData('13', $r('app.string.fruit_banana'), '8431'),
  new RankData('14', $r('app.string.fruit_grapes'), '7909'),
  new RankData('15', $r('app.string.fruit_red_grape'), '7547'),
  new RankData('16', $r('app.string.fruit_pears'), '7433'),
  new RankData('17', $r('app.string.fruit_pineapple'), '7186'),
  new RankData('18', $r('app.string.fruit_durian'), '7023'),
  new RankData('19', $r('app.string.fruit_guava'), '6794'),
  new RankData('20', $r('app.string.fruit_carambola'), '6721')
];


  这里我们首先导入RankData,然后创建了两个数组,数组中通过RankData构建函数进行bean的构建,注意这里的id,我可以使用number也可以使用string,同时资源的引用是 $rr就表示resource,使用app.string引用文字资源,你还可以app.color等一些方式引用其他类型资源,构建了两个数组,然后导出这两个数组在其他文件中使用。


  现在有了模拟数据之后,我们可以再创建一个类去提供模拟数据,在model包下新建一个RankViewModel.ets文件,代码如下所示:

import { RankData } from './RankData';
import { rankData1, rankData2 } from './DataModel';
export class RankViewModel {
  loadRankDataSource1(): RankData[] {
    return rankData1;
  }
  loadRankDataSource2(): RankData[] {
    return rankData2;
  }
}


  这里导入了RankData和DataModel,通过在RankViewModel中进行返回数据得到具体的数据数组。这个其实和Android的MVI架构差不多,下面我们再添加一些colors资源,在后面的样式上会用到,修改base/element/color.json文件,代码如下所示:

{
  "color": [
    {
      "name": "start_window_background",
      "value": "#FFFFFF"
    },
    {
      "name": "white",
      "value": "#FFFFFF"
    },
    {
      "name": "rank_first_gradient_start",
      "value": "#FFFF9A"
    },
    {
      "name": "rank_first_gradient_end",
      "value": "#CCA538"
    },
    {
      "name": "rank_first_border",
      "value": "#9E8A24"
    },
    {
      "name": "rank_first_text",
      "value": "#9E8A24"
    },
    {
      "name": "rank_secondary_gradient_start",
      "value": "#B8B8B8"
    },
    {
      "name": "rank_secondary_gradient_end",
      "value": "#9C9C9C"
    },
    {
      "name": "rank_secondary_border",
      "value": "#7E7E7E"
    },
    {
      "name": "rank_secondary_text",
      "value": "#FFFFFF"
    },
    {
      "name": "rank_third_gradient_start",
      "value": "#B9A185"
    },
    {
      "name": "rank_third_gradient_end",
      "value": "#AE8659"
    },
    {
      "name": "rank_third_border",
      "value": "#775C3E"
    },
    {
      "name": "rank_third_text",
      "value": "#FFFFFF"
    },
    {
      "name": "rank_view_color_holder",
      "value": "#FFFFFF"
    },
    {
      "name": "item_color",
      "value": "#007DFF"
    },
    {
      "name": "item_color_black",
      "value": "#182431"
    },
    {
      "name": "background",
      "value": "#F1F3F5"
    },
    {
      "name": "font_description",
      "value": "#989A9C"
    },
    {
      "name": "circle_text_background",
      "value": "#007dff"
    }
  ]
}


除此之外还有三个图标,你可以在我的源码中获取,放在resources/base/media

f885ed4b65db4c7eb59cd28ddae7dae5.png

  其中icon.png是创建工程时自带的图标,如果你觉得Project模式下文件过多,你可以切换为Ohos模式。

7cf76377679a4dc4a531606a7aba1580.png

这样看起来比较简洁,只不过你需要熟悉文件结构才行。


③ 样式

  在进行鸿蒙应用开发时,通常会将样式和代码进行分离,这一点是很常见了,我们在ets目录下新建一个constants文件夹,该目录下新建一个Constants.ets文件,代码如下:

/**
 * 字体大小
 */
export enum FontSize {
  SMALL = 14,
  MIDDLE = 16,
  LARGE = 20,
};
/**
 * 字体粗细
 */
export enum FontWeight {
  BOLD = '400',
  BOLDER = '500',
};
/**
 * 权重是组件大小的全局默认值。
 */
export const WEIGHT = '100%';
/**
 * Toast 出现的时间
 */
export const TIME = 1000;
/**
 * App退出的间隔时间
 */
export const APP_EXIT_INTERVAL: number = 4500;
/**
 * 页面TAG
 */
export const TAG: string = 'Index';
/**
 * 标题内容
 */
export const TITLE: Resource = $r('app.string.title');
class style {
  RANK_PADDING: number = 15;        // 排名填充
  CONTENT_WIDTH: string = '90%';    // 内容的宽度
  BORDER_RADIUS: number = 20;       // 边界半径
  STROKE_WIDTH: number = 1;         // 描边宽度
  HEADER_MARGIN_TOP: number = 20;   // 距离上边距
  HEADER_MARGIN_BOTTOM: number = 15;// 距离下边距
  LIST_HEIGHT: string = '65%';      // List高度
}
/**
 * 页面样式
 */
export const Style: style = {
  RANK_PADDING: 15,
  CONTENT_WIDTH: '90%',
  BORDER_RADIUS: 20,
  STROKE_WIDTH: 1,
  HEADER_MARGIN_TOP: 20,
  HEADER_MARGIN_BOTTOM: 15,
  LIST_HEIGHT: '65%'
};
class listHeaderStyle {
  FONT_WEIGHT: number = 400;            // 字体粗细
  LAYOUT_WEIGHT_LEFT: string = '30%';   // 左边的布局权重
  LAYOUT_WEIGHT_CENTER: string = '50%'; // 中间的布局权重
  LAYOUT_WEIGHT_RIGHT: string = '20%';  // 右边的布局权重
}
/**
 * 列表标题样式
 */
export const ListHeaderStyle: listHeaderStyle = {
  FONT_WEIGHT: 400,
  LAYOUT_WEIGHT_LEFT: '30%',
  LAYOUT_WEIGHT_CENTER: '50%',
  LAYOUT_WEIGHT_RIGHT: '20%',
};
class itemStyle {
  TEXT_LAYOUT_SIZE: number = 24;          // 文本的行高
  CIRCLE_TEXT_BORDER_RADIUS: number = 24; // 圆形文本的边框半径
  CIRCLE_TEXT_SIZE: number = 24;          // 圆圈文本的大小
  CIRCLE_TEXT_COLOR_STOP_1: number = 0.5; // 渐变色比例1
  CIRCLE_TEXT_COLOR_STOP_2: number = 1.0; // 渐变色比例2
  BAR_HEIGHT: number = 48;                // item高度
  LAYOUT_WEIGHT_LEFT: string = '30%';     // 左边的布局权重
  LAYOUT_WEIGHT_CENTER: string = '50%';   // 中间的布局权重
  LAYOUT_WEIGHT_RIGHT: string = '20%';    // 右边的布局权重
  BORDER_WIDTH: number = 1;               // 边框宽度
  COLOR_BLUE: Resource = $r('app.color.item_color');     // 文字蓝色
  COLOR_BLACK: Resource = $r('app.color.item_color_black');    // 文字黑色
}
/**
 * 列表Item样式
 */
export const ItemStyle: itemStyle = {
  TEXT_LAYOUT_SIZE: 24,
  CIRCLE_TEXT_BORDER_RADIUS: 24,
  CIRCLE_TEXT_SIZE: 24,
  CIRCLE_TEXT_COLOR_STOP_1: 0.5,
  CIRCLE_TEXT_COLOR_STOP_2: 1.0,
  BAR_HEIGHT: 48,
  LAYOUT_WEIGHT_LEFT: '30%',
  LAYOUT_WEIGHT_CENTER: '50%',
  LAYOUT_WEIGHT_RIGHT: '20%',
  BORDER_WIDTH: 1,
  COLOR_BLUE: $r('app.color.item_color'),
  COLOR_BLACK: $r('app.color.item_color_black')
};
class titleBarStyle {
  IMAGE_BACK_SIZE: number = 21;         // 后退按钮的图像大小
  IMAGE_BACK_MARGIN_RIGHT: number = 18; // 后退按钮的右边距
  IMAGE_LOADING_SIZE: number = 22;      // 刷新按钮的图像大小
  BAR_HEIGHT: number = 47;              // 标题栏的高度
  BAR_MARGIN_HORIZONTAL: number = 26;   // 标题组件的水平边距
  BAR_MARGIN_TOP: number = 10;          // 标题组件的上边距
  WEIGHT: string = '50%';               // 行布局的权重
}
/**
 * 标题栏样式
 */
export const TitleBarStyle: titleBarStyle = {
  IMAGE_BACK_SIZE: 21,
  IMAGE_BACK_MARGIN_RIGHT: 18,
  IMAGE_LOADING_SIZE: 22,
  BAR_HEIGHT: 47,
  BAR_MARGIN_HORIZONTAL: 26,
  BAR_MARGIN_TOP: 10,
  WEIGHT: '50%',
};


  这里的代码乍一看很多,不好理解,其实我们分析一下就知道是写什么属性,首先我们定义了页面字体大小和粗细的枚举类型,用于设置标题文字和其他文字,然后就是页面的权重、退出App的提示时间等、接着就是定义页面样式、标题栏样式、列表头样式、列表Item样式,通过注释你可以你知道每一个样式是什么意思,熟能生巧,你现在觉得不适应是因为不熟悉的缘故。


④ 组件

  在ArkTS中组件是一个比较重要的知识点,组件也分为三个类型,基础组件、容器组件和自定义组件。

  • 基础组件,比如Text、Button、Image、TextInput等。
  • 容器组件,比如Column、Row、Stack、List等。
  • 自定义组件,则是根据实际的功能需求,由开发者自己组合使用基础组件和容器组件变成新的功能组件。比如页面的标题栏,左侧是返回按钮,中间是标题文字,可能还会有副标题,右侧是功能按钮,这种就是自定义组件。


Harmony ArkTS语言(下)https://developer.aliyun.com/article/1407895

相关文章
|
6月前
|
JavaScript 前端开发 开发者
深入理解ArkTS:Harmony OS 应用开发语言 TypeScript 的基础语法和关键特性
深入理解ArkTS:Harmony OS 应用开发语言 TypeScript 的基础语法和关键特性
607 0
|
开发框架 API 开发者
HarmonyOS学习路之方舟开发框架—学习ArkTS语言(基本语法 二)
在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。在进行 UI 界面开发时,通常不是简单的将系统组件进行组合使用,而是需要考虑代码可复用性、业务逻辑与UI分离,后续版本演进等因素。因此,将UI和部分业务逻辑封装成自定义组件是不可或缺的能力。
|
25天前
|
JavaScript 前端开发 安全
TypeScript无缝衔接ArkTS:快速入门鸿蒙ArkTS基本语法
【10月更文挑战第12天】TypeScript无缝衔接ArkTS:快速入门鸿蒙ArkTS基本语法
75 0
TypeScript无缝衔接ArkTS:快速入门鸿蒙ArkTS基本语法
|
4月前
|
存储 Dart 开发工具
flutter实现语言的国际化
【7月更文挑战第16天】
84 1
|
6月前
|
API 数据处理 开发者
Harmony ArkTS语言(下)
Harmony ArkTS语言(下)
170 0
|
6月前
|
JavaScript 前端开发 索引
【HarmonyOS 4.0 应用开发实战】TypeScript入门之接口详讲
【HarmonyOS 4.0 应用开发实战】TypeScript入门之接口详讲
92 0
|
开发框架 前端开发 JavaScript
HarmonyOS学习路之方舟开发框架—学习ArkTS语言(基本语法 三)
页面和自定义组件生命周期 在开始之前,先明确自定义组件和页面的关系
HarmonyOS学习路之方舟开发框架—学习ArkTS语言(基本语法 三)
|
开发框架 JavaScript 开发者
HarmonyOS学习路之方舟开发框架—学习ArkTS语言(基本语法 一)
ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,继承了TS的所有特性,是TS的超集。因此,在学习ArkTS语言之前,建议开发者具备TS语言开发能力。
|
开发框架 前端开发 开发者
HarmonyOS学习路之方舟开发框架—学习ArkTS语言(基本语法 五)
如果每个组件的样式都需要单独设置,在开发过程中会出现大量代码在进行重复样式设置,虽然可以复制粘贴,但为了代码简洁性和后续方便维护,我们推出了可以提炼公共样式进行复用的装饰器@Styles。
|
开发框架 开发者
HarmonyOS学习路之方舟开发框架—学习ArkTS语言(基本语法 四)
当创建了自定义组件,并想对该组件添加特定功能时,例如在自定义组件中添加一个点击跳转操作。若直接在组件内嵌入事件方法,将会导致所有引入该自定义组件的地方均增加了该功能。为解决此问题,ArkUI引入了@BuilderParam装饰器,@BuilderParam用来装饰指向@Builder方法的变量,开发者可在初始化自定义组件时对此属性进行赋值,为自定义组件增加特定的功能。该装饰器用于声明任意UI描述的一个元素,类似slot占位符。