上一篇文章 : 鸿蒙入门极速版:超简洁保姆级教程!纯干货,快收藏!
欢迎关注本专栏 :鸿蒙开发实践-基础入门
简介
大家好,我是石小石!在上一节教程中,我们使用类js的web开发范式实现了一个模拟表盘的效果:
本教程,我们将使用鸿蒙原生ArkTs语言实现一个记事本功能开发,帮助大家迅速上手、入门鸿蒙应用开发。
通过本教程您将学习到以下知识点:
- 快速构建项目应用
- 了解鸿蒙项目框架
- 学会使用ArkUI组件实现页面布局
- 学会使用路由实现页面跳转
- 学会使用函数实现记事本的增删
- .......
知识回顾
HarmonyOS SDK
HarmonyOS SDK是一个开放能力合集,提供应用框架、应用服务、系统、媒体、AI、图形在内的六大领域丰富完备的能力。
简单来说,借助它我们可以实现元服务开发 、鸿蒙原生应用开发!
鸿蒙原生应用开发
通俗的来水,鸿蒙的原生应用包含手机、Pad、TV、车载智慧屏和穿戴设备。理论上,开发一个应用,在这些不同的终端都是可以运行的。
要开发原生应用,我们还需要知道三个基础的东西:
- DevEco Studio
简单来说,就是像IDEA的代码编译器
- ArkTs
ArkTS是鸿蒙生态的应用开发语言,它在保持TypeScript(前端狂喜,哈哈哈)基本语法风格的基础上,进一步通过规范强化静态检查和分析.....
ArkTs由 Ark和 TypeScript两个单词组成。Ark 英[ɑːk] ,译文方舟。所以,我们可以叫ArkTs为方舟编程语言。
- ArkUI
ArkUI是一套构建分布式应用界面的声明式UI开发框架。简单来说,ArkUI是基于ArkTS的声明式的UI开发范式, 是ArkTS语言的一个具体应用或扩展,用于构建用户界面。ArkTs和ArkUI前端同学可以简单理解为js和element UI的关系。
构建第一个应用
官方api:文档中心
前言
Arkts的基本语法与前端得Typescript 风格非常相似,本文默认大家掌握基本的Typescript语法,因此,不会对Arkts的基本语法过多阐述。下一文章,我们将详细介绍Arkts语言特性。
工程创建
- 点击Create Project创建工程
- 选择Application应用(tomic Service为元服务开发)
注意,后面有很多模板,模板上的图标代表这个应用可以在哪些终端运行
注:mac软件默认是英文,可以设置为中文:developer.huawei.com/consumer/cn...
项目预览
要预览默认的项目,我们点击右侧的【预览器】按钮即可。
工程目录结构
我们采用默认模板创建的项目,它的项目结构如下
js
├── .hvigor # 存放编译构建相关的临时文件或配置
├── .idea # IDE(如IntelliJ IDEA)的配置目录
├── AppScope
│ └── entry # HarmonyOS工程模块,编译构建生成一个HAP包
│ ├── src
│ │ ├── main
│ │ │ └── ets
│ │ │ ├── entryability # 应用/服务的入口
│ │ │ │ └── [ArkTS源码文件]
│ │ │ └── entrybackupability # 应用提供扩展的备份恢复能力
│ │ │ └── [ArkTS源码文件]
│ │ ├── pages # 应用/服务包含的页面
│ │ │ └── Index.ets # 页面源码文件
│ │ └── resources # 存放应用/服务所用到的资源文件
│ │ └── mock
│ │ └── ohosTest
│ │ ├── test
│ │ │ └── .gitignore
│ │ ├── build-profile.json5 # 模块信息、编译信息配置项
│ │ ├── hvigorfile.ts # 模块级编译构建任务脚本
│ │ ├── obfuscation-rules.txt # 混淆规则文件
│ │ └── oh-package.json5 # 描述包名、版本、入口文件和依赖项等信息
│ └── module.json5 # 模块配置文件
├── .gitignore # Git忽略文件配置
├── build-profile.json5 # 工程级配置信息
├── hvigorfile.ts # 工程级编译构建任务脚本
├── obfuscation-rules.txt # 混淆规则文件
├── oh-package.json5 # 描述全局配置,如依赖覆盖、依赖关系重写和参数化配置
└── oh_modules # 存放三方库依赖信息
├── .gitignore
├── build-profile.json5 # 三方库编译信息配置
├── hvigorfile.ts # 三方库编译构建任务脚本
├── local.properties # 本地属性配置(如SDK路径)
└── oh-package-lock.json5 # 锁定依赖版本,确保项目依赖的一致性
对于我们初学者,它的目录可以简化如下
js
├── AppScope
│ └── entry # HarmonyOS工程模块,编译构建生成一个HAP包
│ ├── src
│ │ ├── main # 应用/服务的入口
│ │ ├── pages # 应用/服务包含的页面(写页面逻辑的地方)
│ │ │ └── Index.ets # 页面源码文件
│ │ └── resources # 存放应用/服务所用到的资源文件
│ └── module.json5 # 模块配置文件
我们只需要关注在pages下进行多页面开发,在module.json5中进行页面配置即可。
代码简析
在正式开发前,我们先简单学习一下默认模板的代码,熟悉一下Artks的UI范式语法
js
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
RelativeContainer() {
Text(this.message)
.id('HelloWorld')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
}
.height('100%')
.width('100%')
}
}
默认的代码很少,效果也很简单,但是对于只写js和不写样式的后端来说可能都有些陌生。
没关系,我们看一个代码结构图
- 装饰器: 对后端来说装饰器在简单不过了,对于前端来说,我们可以理解为它用于装饰类、结构、方法以及变量,并赋予他们特殊的含义。如上述示例中@Entry、@Component和@State都是装饰器,@Component表示自定义组件,@Entry表示该自定义组件为入口组件,@State表示组件中的状态变量,状态变量变化会触发UI刷新。
- UI描述:以声明式的方式来描述UI的结构,例如build()方法中的代码块。
- 自定义组件:可复用的UI单元,可组合其他组件,如上述被@Component装饰的struct Index。
- 系统组件:ArkUI框架中默认内置的基础和容器组件,可直接被开发者调用,比如示例中的Column、Text、Divider、Button。
- 属性方法:组件可以通过链式调用配置多项属性,如fontSize()、width()、height()、backgroundColor()等。
- 系统组件、属性方法、事件方法具体使用可参考基于ArkTS的声明式开发范式。
构建第一个页面
页面简述
我们的第一个页面很简单,就是一个按钮,点击可以进行跳转。所以我们只需要实现两个核心功能
- 完成页面布局,添加按钮
- 给按钮添加点击跳转事件
在开发第一个页面前,我们先删除默认的模板代码,让框架更加清晰
js
@Entry
@Component
struct Index {
build() {
}
}
页面布局
我们的按钮应该显示页面正中间,我们可以借助ArkUI的Row容器组件进行页面的布局
js
@Entry
@Component
struct Index {
build() {
Row() {
// 子组件写在这里....
}.width('100%').height('100%').backgroundColor('#1677ff').alignItems(VerticalAlign.Center).justifyContent(FlexAlign.Center)
}
}
width、height、backgroundColor、alignItems等等这些属性对前端来说应该是非常熟悉的。上述代码中,我们使用alignItems的VerticalAlign.Cente属性和justifyContent的FlexAlign.Center保证按钮能垂直居中展示在页面。
现在,我们参考ArkUI的button组件,给页面添加一个按钮。
注意,我们在Button内部使用了Text组件,用于展示文字。
看起来,按钮样式有点丑,我们给Button组件美化一下,加点样式
js
@Entry
@Component
struct Index {
build() {
Row() {
// 添加按钮组件
Button() {
// 文本组件
Text('你好,鸿蒙')
.fontSize(22)
.fontColor('#fff')
.fontWeight(FontWeight.Bold)
}
// 按钮组件的类型(默认是50%圆角的)
.type(ButtonType.Normal)
// 按钮的圆角级别
.borderRadius('10%')
.margin({
top: 20
})
// 按钮的背景色
.backgroundColor('#ff9c6e')
.width('40%')
.height('5%')
}
.width('100%')
.height('100%')
.backgroundColor('#d4380d')
.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Center)
}
}
现在看起来,样式好看很多
实现页面间的跳转
页面间的导航可以通过页面路由router来实现。页面路由router根据页面url找到目标页面,从而实现跳转。使用页面路由请导入router模块。
路由的概念和vue、react的路由十分接近,因此,前端只需要了解写法即可
假设我们第二个页面的路由是pages/Second,那我们只需给代码添加如下的点击事件
js
// 导入页面路由模块
import { router } from '@kit.ArkUI';
@Entry
@Component
struct Index {
build() {
Row() {
// 添加按钮组件
Button() {
// 文本组件
Text('你好,鸿蒙')
.fontSize(22)
.fontColor('#fff')
.fontWeight(FontWeight.Bold)
}
// 跳转按钮绑定onClick事件,点击时跳转到第二页
.onClick(() => {
// 跳转到第二页
router.pushUrl({ url: 'pages/Second' })
})
}
.width('100%')
.height('100%')
.backgroundColor('#d4380d')
.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Center)
}
}
OK,现在我们去实现第二个页面。
构建第二个页面
创建第二个页面
新在"Project "窗口,右键点击"pages "文件夹,选择"New > ArkTS File ",命名为"Second ",点击回车键 。
创建好后,我们给第二个页面先简单写个内容
js
// Second.ets
@Entry
@Component
struct Second {
@State message: string = '这是第二个页面'
build() {
Row() {
Column() {
Text(this.message)
}
.width('100%')
}
.height('100%')
}
}
配置第二个页面的路由
在"entry > src > main > resources > base > profile"路径下,我们可以在main_pages.json文件中的"src"下配置第二个页面的路由"pages/Second"。
js
{
"src": [
"pages/Index",
"pages/Second"
]
}
路由配置好后,我们就可以试一试页面的跳转功能了。
页面简述
根据我们的需求
页面应该有一个输入框、一个添加按钮、一个同于展示数据的列表。
输入框我们可以使用ArkUI的TextInput组件,添加按钮使用Button组件、列表使用List及ListItem组件
页面基本布局实现
按照需求,我们先实现基本的页面布局及样式的编写
js
// pages/NotesPage.ets
@Entry
@Component
struct NotesPage {
// 保存事项的数组
@State notes: string[] = ['事项1','事项2','事项3'];
// 保存新事项的输入内容
@State newNote: string = '';
build() {
Column() {
//应用名
Text('石小石的记事本')
.fontSize(24)
.fontColor('#003eb3')
.margin({ top: 20, bottom: 20 });
// 文本输入
TextInput({
// text 绑定到 newNote 状态,用于存储输入内容。
text: this.newNote,
placeholder: '添加新的事项',
}).width('80%').margin({ bottom: 10 }).padding(10).fontSize(16).borderColor('#ccc').borderWidth(1).borderRadius(5)
// 添加按钮
Button('添加').width('80%').padding(10).fontSize(16).backgroundColor('#007AFF').fontColor('#FFF').borderRadius(5)
// 渲染列表
List() {
ForEach(this.notes,(item:string,index:number)=>{
ListItem(){
Row(){
Text(item).fontColor('#001d66')
Button('删除').fontSize(14).height(18).backgroundColor('#ff4d4f').type(ButtonType.Normal).borderRadius('15%').padding({left:10,right:10})
}.width("100%").justifyContent(FlexAlign.SpaceBetween).backgroundColor('#bae0ff').margin({bottom:5}).padding(5)
}
})
}.width('80%').margin({ top: 20 })
}.width('100%').backgroundColor('#e6f4ff').height("100%")
}
}
样式效果
上述代码中,我们使用notes定义了一个初始化列表,使用List组件
和 ForEach
方法 用于渲染事项列表。
forEach的渲染方法可以参考官方文档:ForEach:循环渲染
实现添加功能
实现添加功能,核心有两点:
- input输入框监听输入的值覆给newNote
- 点击button将输入框的值newNote添加到notes
同属性的写法一样,按钮的点击事件可以直接采用链式调用写在Button组件后面,像这样
js
Button('添加')
.onClick(()=> this.addNote())
.width('80%').padding(10).fontSize(16)
TextInput组件的text属性就是事实输入的值,因此,添加功能的实现如下:
js
// pages/NotesPage.ets
@Entry
@Component
struct NotesPage {
@State notes: string[] = ['事项1','事项2','事项3'];
@State newNote: string = '';
// 添加事项
addNote() {
if (this.newNote.trim() !== '') {
this.notes.push(this.newNote)
this.newNote = ''
}
}
build() {
Column() {
// ...
TextInput({
text: this.newNote,
placeholder: '添加新的事项',
}).onChange((value:string)=>{
this.newNote = value
}).width('80%').margin({ bottom: 10 }).padding(10).fontSize(16).borderColor('#ccc').borderWidth(1).borderRadius(5)
Button('添加')
.onClick(()=> this.addNote())
.width('80%').padding(10).fontSize(16).backgroundColor('#007AFF').fontColor('#FFF').borderRadius(5)
// ...
}.width('100%').backgroundColor('#e6f4ff').height("100%")
}
}
可以看出,添加事项的方法addNote和前端js的写法几乎是没有任何差别的!
删除功能
和添加功能类似,我们很容易写出删除功能的代码逻辑
js
// pages/NotesPage.ets
@Entry
@Component
struct NotesPage {
@State notes: string[] = [];
@State newNote: string = '';
// ....
deleteNote(index: number) {
this.notes.splice(index, 1);
}
build() {
Column() {
// ....
List() {
// 循环遍历
ForEach(this.notes,(item:string,index:number)=>{
ListItem(){
Row(){
Text(item).fontColor('#001d66')
// 删除按钮
Button('删除')
.onClick(()=>this.deleteNote(index))
}.width("100%")
}
})
}.width('80%').margin({ top: 20 })
}.width('100%').backgroundColor('#e6f4ff').height("100%")
}
}
完整代码及效果演示
- 初始页
js
// 导入页面路由模块
import { router } from '@kit.ArkUI';
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
Row() {
// 添加按钮组件
Button() {
// 文本组件
Text('你好,鸿蒙')
.fontSize(22)
.fontColor('#fff')
.fontWeight(FontWeight.Bold)
}
// 按钮组件的类型(默认是50%圆角的)
.type(ButtonType.Normal)
// 按钮的圆角级别
.borderRadius('10%')
.margin({
top: 20
})
// 按钮的背景色
.backgroundColor('#ff9c6e')
.width('40%')
.height('5%')
// 跳转按钮绑定onClick事件,点击时跳转到第二页
.onClick(() => {
router.pushUrl({ url: 'pages/Second' })
})
}
.width('100%')
.height('100%')
.backgroundColor('#d4380d')
.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Center)
}
}
- 记事本页
js
// pages/NotesPage.ets
@Entry
@Component
struct NotesPage {
@State notes: string[] = ['事项1','事项2','事项3'];
@State newNote: string = '';
addNote() {
if (this.newNote.trim() !== '') {
this.notes.push(this.newNote)
this.newNote = ''
}
}
deleteNote(index: number) {
this.notes.splice(index, 1);
}
build() {
Column() {
Text('石小石的记事本')
.fontSize(24)
.fontColor('#003eb3')
.margin({ top: 20, bottom: 20 });
TextInput({
text: this.newNote,
placeholder: '添加新的事项',
}).onChange((value:string)=>{
this.newNote = value
}).width('80%').margin({ bottom: 10 }).padding(10).fontSize(16).borderColor('#ccc').borderWidth(1).borderRadius(5)
Button('添加')
.onClick(()=> this.addNote())
.width('80%').padding(10).fontSize(16).backgroundColor('#007AFF').fontColor('#FFF').borderRadius(5)
List() {
ForEach(this.notes,(item:string,index:number)=>{
ListItem(){
Row(){
Text(item).fontColor('#001d66')
Button('删除')
.onClick(()=>this.deleteNote(index))
.fontSize(14).height(18).backgroundColor('#ff4d4f').type(ButtonType.Normal).borderRadius('15%').padding({left:10,right:10})
}.width("100%").justifyContent(FlexAlign.SpaceBetween).backgroundColor('#bae0ff').margin({bottom:5}).padding(5)
}
})
}.width('80%').margin({ top: 20 })
}.width('100%').backgroundColor('#e6f4ff').height("100%")
}
}
总结
本教程借助原生ArkTS结合ArkUI组件实现了一个简单的欢迎页及记事本页。现在,我们在梳理一下文中用到的一些知识点
- 代码结构
- ArkUI组件
借助ArkUI组件,我们可以快速实现页面布局,添加按钮等输入框。组件的基础用法如下
js
// Row布组件
Row() {
// 按钮组件
Button() {
// 文本组件
Text('你好,鸿蒙')
.fontSize(22)
.fontColor('#fff')
.fontWeight(FontWeight.Bold)
}
}
.width('100%') //Row组件的样式
.height('100%')
.backgroundColor('#d4380d')
.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Center)
具体的参数可参考ArkUI文档。
- 路由跳转
在main_pages.json文件中的"src"下配置路由"pages/Second"。
js
{
"src": [
"pages/Index",
"pages/Second"
]
}
代码中实现点击跳转
js
Button() {
// ...
}.onClick(() => {
router.pushUrl({ url: 'pages/Second' })
})
- Arkts基本语法
语法同Typescript
添加、删除事件
js
@State notes: string[] = [];
@State newNote: string = '';
addNote() {
if (this.newNote.trim() !== '') {
this.notes.push(this.newNote)
this.newNote = ''
}
}
deleteNote(index: number) {
this.notes.splice(index, 1);
}
// ....
Button('添加').onClick(()=> this.addNote())
Row(){
Text(item).fontColor('#001d66')
Button('删除')
.onClick(()=>this.deleteNote(index))
}
OK,本节教程到这里就结束了!欢迎大家关注本专栏:鸿蒙开发实践-基础入门,下次更新不迷路哈!
下篇文章预告:
Arkts的语法特性及使用