HarmonyOS 学习笔记
鸿蒙开发
项目结构

在pages同级可以创建一个文件夹,命名viewModel,用于存放类型
export default class ShopInfo {
title: string
}在pages统计可以创建一个文件夹,明明 model,用于编写 pages中的方法,比如要发送接口,可以这么写:
import http from '@ohos.net.http';
import ShopInfo from '../viewmodel/ShopInfo'
class ShopModel {
baseUrl: string = 'http://localhost:3000'
pageNo: number = 1
getShopList(): Promise<ShopInfo[]> {
return new Promise((resolve, reject) => {
let httpRequest = http.createHttp()
httpRequest.request(
`${this.baseUrl}/shops?pageNo=${this.pageNo}&pageSize=3`,
{
method: http.RequestMethod.GET
}
)
.then((response: http.HttpResponse) => {
if(response.responseCode === 200) {
resolve(JSON.parse(response.result.toString()))
}else {
console.log('查询失败')
}
})
.catch((err: Error) => {
console.log('查询失败')
})
})
}
}
const shopmodel = new ShopModel()语法
@State:自监控一个变量,有点像是vue的数据绑定,当这个数据改变时,页面上的内容也会同步更新
@Entry:代表这是一个入口型的组件,可以通过页面去访问,而不是只能用于别的页面调用(页面组件)
@Component:代表这是一个自定义组件
循环控制
ForEach
用法:
ForEach(
arr: Array, // 要遍历的数组数据
(item: any, index?: number) => { // 页面组件生成函数
Row() {
}
},
(item: any, index?: number): string => {
// 键生成函数,为数组每一项生成一个唯一标识,相当于 Vue 中 v-for 中的key
return String(index)
}
)自定义组件
在 etc 目录下新建 components 目录
使用 @Component 定义一个组件
导出:
@Component
export struct HeaderComponent {
private title: string = '商品列表';
build() {
// 商品列表文字
Row() {
Image($r('app.media.back'))
.width(30)
Text(this.title)
.fontSize(30)
.fontWeight(FontWeight.Bold)
Blank()
Image($r('app.media.refresh'))
.width(30)
}
.margin(10)
.width('100%')
.height(30)
}
}导入:
import {HeaderComponent} from '../components/HeaderComponent'
HeaderComponent({ title: '收藏信息'})传递参数
在子组件中,使用 private 定义一个变量,并可以赋值,使用 this.属性 读取该属性
在调用子组件时,在括号中传递一个对象,对象就是这个属性,传递什么就是什么
自定义构建函数
使用关键词 @Builder定义,这个就相当于 React 中的把渲染单独拎出来,弄成一个函数,返回值就是元素。
⚠注意:这里不需要返回,不需要return。
用法1:
这样用需要写在 struct 外部,可以在任何地方使用
// 全局自定义构建函数
@Builder function ItemCard(item: goodItem) {
Row({ space: 10}) {
Image($rawfile(item.imgUrl))
.height(80)
Column({space: 4}) {
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
if(item.discount !== 0) {
Text('¥' + item.price)
.fontSize(18)
.fontColor('gray')
.decoration({type: TextDecorationType.LineThrough})
Text('折扣价:¥' + (item.price - item.discount))
.fontSize(18)
.fontColor('red')
}else {
Text('¥' + item.price)
.fontSize(18)
.fontColor('red')
}
}
.alignItems(HorizontalAlign.Start)
}
.shadow({radius: 10, color: 'black', offsetX: 0, offsetY: 0})
.padding(10)
.borderRadius(10)
.height(120)
.backgroundColor('white')
.width('100%')
}
然后在遍历中使用:
List({ space: 8 }) {
ForEach(
this.goodsList,
(item, index) => {
ListItem() {
ItemCard(item)
}
},
(item, index) => {
return String(index)
}
)
}用法2:
写在 struct 内部,不需要加 function
使用时,需要使用this.
@Builder ItemCard(item: goodItem) {}自定义公共样式
可以把样式抽取出来,抽取为公共样式,使用关键词@Styles
⚠注意:抽取出来的公共样式,必须是全部可以使用的属性,如果是 fontSize、fontColor 这种,会报错 ①
用法1:
写在 struct 外部,作为全局使用
@Styles function fillScreen() {
.width('100%')
.padding(10)
.backgroundColor('#ffcacaca')
}Row() {
}
.fillScreen()用法2:
写在 struct 内部,作为当前组件局部使用
@Styles fillScreen() {
.width('100%')
.padding(10)
.backgroundColor('#ffcacaca')
}①:如果想要解决由于公共样式(类似 fontSize、fontColor 这种不同步产生的问题),需要使用@Extend关键词,用法:
@Extend(Text) function priceText() {
.fontColor('gray')
.fontSize(18)
}状态管理
@State
使用
@State装饰器标记的变量必须进行初始化,不能为空值⚠如果是对象中嵌套对象,修改其中对象的属性值,不会触发渲染
⚠数组中的元素更改,不会造成重新渲染;重新赋值可以,但更改不会造成重新渲染
使用@State装饰的数据,可以传递给子组件,但当父组件改变数据时,子组件无法感知到
@Prop
✅ 数据的单向传递
使用@Prop在子组件中定义数据,父组件使用@State定义的数据传递给子组件,子组件可以感知到父组件传递数据的变化。
⚠️注意:@Prop为单向传递,仅能从父组件传递子组件。
⚠️注意:@Prop只支持传递string、number、boolean、enum类型。
父组件是对象类型,子组件可以是对象中的属性。
不可以传递数组、any类型。
用法:
@Entry
@Component
struct father {
@State finishTask: number = 0;
@State totalTask: number = 0;
TaskStatistics({
finishTask: this.finishTask,
totalTask: this.totalTask
})
}@Component
struct TaskStatistics {
@Prop finishTask: number;
@Prop totalTask: number;
}@Link
✅ 数据双向传递
使用@Link在子组件中定义数据,父组件使用@State定义的数据传递给子组件,子组件可以感知到父组件传递数据的变化,同时子组件修改该数据,父组件也会感知到进行重新渲染。
使用
⚠️注意:@Link传递的数据,必须使用$进行装饰,不需要使用this。
⚠️注意:不同于@Prop,@Link可以传递大多数类型,string、number、boolean、enum、object、class、以及数组。
数组中的元素增、删、改也会引起刷新。
嵌套类型以及数组中的对象属性改变不会触发视图更新。
@Entry
@Component
struct father {
@State finishTask: number = 0;
@State totalTask: number = 0;
TaskStatistics({
finishTask: $finishTask,
totalTask: $totalTask
})
}@Component
struct TaskStatistics {
@Link finishTask: number;
@Link totalTask: number;
}@Provide 和 @Consume
✅ 数据双向传递
类似于发布订阅模式,父组件直接将需要传递给后代组件的数据使用 @Provide 进行装饰;哪个子组件需要这个数据,就使用 @Consume 进行接收。
class StatInfo {
finishTask: number = 0
totalTask: number = 0
}@Entry
@Component
struct father {
@Provide stat: StatInfo = new StateInfo();
TaskStatistics()
}@Component
struct TaskStatistics {
@Consume stat: number;
@Link totalTask: number;
}@ObjectLink 和 @Observed
✅ 双向数据传递
@ObjectLink和@Observed用于在嵌套对象或数组元素为对象的场景中进行双向数据同步。
在上边的内容中,当修改的数据为数组中的对象属性时,无法监测到变化,这个就是用于解决问题的。
如果在一个类中引入了另外一个类,则需要将每个类都添加上@Observed。
@Observed
class Person {
name: string
age: number
gf: number
constructor(name: string, age: number, gf?: Person){
this.name = name
this.age = age
this.gf = gf
}
}@Entry
@Component
struct Parent {
@State p: Person = new Person('Jack', 21, new Person('Rose', 18))
build() {
Column() {
Child({p: this.p.gf})
.onClick(() => this.p.gf.age++)
}
}
}@Component
struct Child {
@ObjectLink p: Person
build() {
Column() {
Text(`${this.p.name} : ${this.p.age}`)
}
}
}⚠️注意:
在开发过程中,我们可能你会遇到这个场景:
一个方法在父组件中(比如计算父组件中的数据)
现在希望在子组件中调用这个父组件的方法
那么,我们可以将父组件中的这个方法,传递给子组件
父组件:
TaskItem({item: item, onTaskChange: this.handleTaskChange.})
子组件:
onTaskChange: () => void
但是这样,可能会出现一个问题,当父组件这个方法中使用 this 时,由于父组件中的 this 指向的为父组件,如果直接传递给子组件,那饿这个 this 为子组件,可能会出现 undefined 的报错
这种情况下,需要绑定方法 this 指向后,再传递:
TaskItem({item: item, onTaskChange: this.handleTaskChange.bind(this)})
✅ 使用 bind 改变指向后,传递。
路由
基本路由
在鸿蒙中,使用router进行路由管理。
页面栈的上限为 32 个页面,使用 router.clear() 方法可以清空页面栈
Router有两种页面跳转模式:
router.pushUrl():压入页面栈,可以使用 router.back() 返回当前页。
router.replaceUrl():替换当前页。
Router有两种页面实例模式:
Standard:标准实例模式,每次跳转都会新建一个目标页并压入栈顶(默认模式)
(推荐)Single:单实例模式,如果目标页已经在栈中,则离栈顶最近的同Url页面会被移动到栈顶并重新加载。
跳转用法:
router.pushUrl(
// RouterOptions
{
// url: 目标页面路径
url: 'pages/ImagePage',
// params: 传递的参数(可选)
params: {id: 1}
},
// 页面模式:RouterMode枚举
router.RouterMode.Single,
// 异常毁掉函数,错误码:
// - 100001: 内部错误,可能是渲染失败
// - 100002: 路由地址错误
// - 100003: 路由栈数量超过32
err => {
if(err) {
console.log('路由跳转失败')
}
}
)
✅ 获取页面路由参数:router.getParams()✅ 返回上一页:router.back()
✅ 返回到指定页,并携带参数:
router.back(
{
url: 'pages/Index',
params: {id: 10}
}
)路由配置
在main/resources/profile/main_pages.json中配置页面路由。
将pages下的文件都配置到当前文件 。
动画
属性动画
可以通过添加 .animate 添加动画。
哪个元素,后续状态会出现变化,就添加上这个属性。
用法:
Text('^_^')
.postion({
x: 10,
y: 0
})
.rotate({
angle: 0, // 旋转角度
centerX: '50%', // 旋转中心横坐标
centerY: '50%' // 旋转中心纵坐标
})
.animation({
duration: 1000, // 动画持续时间
curve: Curve.EaseInOut // 变化曲线
})属性:

显式动画
显式动画是通过全局animateTo函数来修改组件属性,实现属性变化时的渐变过渡效果。
用法:
@State postionX: number = 100
@State postionY: number = 10
Text('^_^')
.postion({
x: this.positionX,
y: this.positionY
})
.rotate({
angle: 0, // 旋转角度
centerX: '50%', // 旋转中心横坐标
centerY: '50%' // 旋转中心纵坐标
})
// 显式调用animateTo函数触发动画
animateTo(
{duration: 1000},
() => {
// 修改组件属性关联的状态变量
// 这样就会自动进行更新,并添加动画
this.positionX -= 10
}
)属性同上
组件转场动画
组件转场动画是在组件插入或移除时的过渡动画,通过组件的transition属性来配置。
注意⚠️:transition只是设置一种离场方式,还是需要使用animateTo进行调用。
用法:
@State isShow: boolean = true
...
if(this.isShow){
Text('^_^')
.transition({
opcity: 0,
rotate: {angle: -360},
scale: {x: 0, y: 0}
})
}
...
// 显式调用 animateTo 函数触发动画
animateTo(
{duration: 1000},
() => {
this.isShow = false
}
)属性:

Stage模型
Stage模型概述

对于一个鸿蒙应用,分为两个模块
Ability Module:会被编译为 HAP 模块,每一个都可以一个独立的运行。Library Module:会被编译为 HSP 模块
Stage应用配置文件
在全局项目中,
AppScope/app.json5(应用的全局配置信息)在每个模块中,在
resources/module.json5(Module的配置信息)
UIAbility生命周期

Foreground:前台,指当前手机打开此页面
Background:后台,只手机切换应用,已不在此应用
Destory:用户退出应用
页面及组件生命周期

页面生命周期钩子仅能在页面中使用,也就是加了 @Entry 的组件
使用
router.push不会造成页面销毁(页面显示隐藏),使用router.replace会造成页面销毁
UIAbility的启动模式

✅ Singleton模式:一个 UIAbility 只存在一个实例,任务列表不会出现多个
✅ standard模式:一个 UIAbility 会存在多个实例,每次都会重新创建
以上两种模式,只需要在 module.json5 文件中,配置 lunchType 关键词即可
✅ specified模式:每个 UIAbility 会有一个key标识,如果启动的是这个 key 的,直接拉起,如果没有这个 key 的实例,则创建。
❓ 如何实现第三种模式(specified)?
答:比如说我们要实现这样一个功能,一个文档列表页面,当我们点击新建文档时,我们希望跳转到新建文档的Abliity,这时我也可以通过任务列表返回当前的文档列表Ability。
分为三步:
在当前文档列表页面编辑(编写跳转Ability)
在 ets 文件夹下右键,新建 Ability,并起名
在 class 中获取上下文
private context = getContext(this) as common.UIAbilityContext在新建文档页面按钮点击事件上绑定方法:
// 指定要跳转到的UIAbility
let want: Want = {
deviceId: '', // deviceId为空表示本设备
bundleName: 'com.example.myapplication', // 在 AppScope/app.json5 中的 bundleName
moduleName: 'entry', // 跳转到哪个模块,在 module.json5 中的 name
abilityName: '', // 跳换到哪个Ability去,在 module.json5 中a bilities 数组中,可以找到name
parameters: {
instanceKey: `idx_${this.index++}` // 唯一的key
}
}跳转到 Ability 去
this.context.startAbility(want)创建AbilityStage
在 ets 文件夹下,创建文件夹,起名 myabilitystage
在新建文件夹下,创建 MyAbilityStage.ts文件
编写 stage 代码:
import AbilityStage from '@ohos.app.ability.AbilityStage'
import Want from '@ohos.app.ability.Want'
export default class MyAbilityStage extends AbilityStage {
onAcceptWant(want: Want): string {
if(want.abilityName === 'DocumentAbility') {
return `DocAbilityInstance_${want.parameters.instanceKey}`
}
return ''
}
}在 module.json5 中配置,指定舞台
{
"moudle": {
...
"srcEntry": './ets/myabilitystage/MyAbilityStage.ts'
...
}
}网络请求
http网络请求
导入
http模块
import http from '@ohos.net.http
使用
http模块发送请求,处理响应
let httpRequest = http.createHttp()
httpRequest.request(
'http://localhost:3000/users,
{
// 请求方法
method: http.RequestMethod.GET,
// 请求参数
extraData: {'param1': 'value1'}
}
)
// 请求成功
.then((resp: http.HttpResponse) => {
if(resp.responseCode === 200) {
}
})
// 请求失败
.catch((err: Error) => {
})HttpRequestOptions:
method: RequestMethod:请求方式extraData: string | Object:请求参数(当传递string时,将会拼接到路径上,相当于get请求)header: Object:请求头字段connectTimeOut: number:连接超时时间,单位毫秒,默认是60000msreadTimeout: number:读取超时间,同上
axios第三方网络请求
下载和安装
ohpm:鸿蒙中的包管理工具,相当于 npm下载和安装
axios:
下载axios:
ohpm install @ohos/axios开放网络权限
"requestPermissions": [{
"name": "ohos.permission.INTERNET"
}]所有的三方库:
所有下载的依赖,将会显示在oh-package.json5文件中(相当于package.json文件)
‼️安装后出现一个问题:ERROR: The compatibleSdkVersion 9 cannot be smaller than version 12 declared in library [:library] as the library might be using APIS not available in 9
这是因为版本不匹配,如果下载的是 2.2.2 ,则在oh-package.json5文件中修改成 2.2.0
使用时,首先导入 axios(import axios from '@ohos/axios),其余和平时使用一样
数据持久化
用户首选项
用户首选项(Preference)为应用提供 key-value 键值型的数据处理能力,支持应用持久化轻量级数据。
使用:
导入首选项模块
import dataPreference from '@ohos.data.preferences'获取首选项实例,读取指定文件
dataPreference.getPreferences(this.context, 'MyAppPreferences')
.then(preferences => {
// 获取成功
})
.catch(reason => {
// 获取失败
})数据操作
// 3.1 写入数据,如果已经存在则会覆盖,可利用 .has() 查看是否存在
preferences.put('key', val)
.then(() => preferences.flush())
.catch(reason => {})
// 3.2 删除数据
preferences.delete('key')
.then(() => {})
.catch(reason => {})
// 3.3 查询数据
preferences.get('key', 'defaultValue')
.then(value => console.log('查询成功')
.catch(reason => console.log('查询失败')说明:
Key 为 string 类型, 要求非空且长度不超过80字节
Value 可以是 string、number、boolean 及以上类型数组,大小不超过 8192 字节
数据量建议不超过一万条
详细使用:
在 common/utils 文件夹中新建 PreferencesUtil.ts 文件,编写代码
import preferences from '@ohos.data.preferences';
class PreferencesUtil {
prefMap: Map<string, preferences.Preferences> = new Map()
async loadPreference(context, name: string) {
try {
let pref = await preferences.getPreferences(context, name);
this.prefMap.set(name, pref)
console.log('testTag', `加载Preferences[${name}]成功`)
}catch (e) {
console.log('testTag', '失败')
}
}
// 存
async putPreferenceValue(name: string, key: string, value: preferences.ValueType) {
if(!this.prefMap.get(name)) {
console.log('testTag', `Preferences[${name}]未初始化`)
return
}
try {
let pref = this.prefMap.get(name)
// 写入操作
await pref.put(key, value)
// 刷盘
await pref.flush()
console.log('testTag', `Preferences[${name}]保存成功`)
}catch (e) {
console.log('testTag', `Preferences[${name}]保存失败`)
}
}
// 读
async getPreferenceValue(name: string, key: string, defaultValue: preferences.ValueType) {
if(!this.prefMap.get(name)) {
console.log('testTag', `Preferences[${name}]未初始化`)
return
}
try {
let pref = this.prefMap.get(name)
// 读数据
let value = await pref.get(key, defaultValue)
// 刷盘
await pref.flush()
console.log('testTag', `Preferences[${name}]读取成功`)
return value;
}catch (e) {
console.log('testTag', `Preferences[${name}]读取失败`)
}
}
}
const preferencesUtil = new PreferencesUtil()
export default preferencesUtil as PreferencesUtil在入口文件 EntryAbility.ts 中,在 onCreate 钩子中,编写代码
async onCreate(want, launchParam) {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
// 加载Preferences
await PreferencesUtil.loadPreference(this.context, 'MyPreferences')
}在需要存入的地方编写:
preferencesUtil.putPreferenceValue('MyPreferences', 'sliderFontSize', value)在需要取出的地方:
await preferencesUtil.getPreferenceValue('MyPreferences', 'sliderFontSize', 40) as number
关系型数据库
初始化数据库:

增、删、改数据:

查询数据:

使用代码:
编写初始化、增删改查等操作
import relationalStore from '@ohos.data.relationalStore';
import TaskInfo from '../viewmodel/TaskInfo';
class TaskModel {
private rdbStore: relationalStore.RdbStore
private tableName: string = 'TASK'
/**
* 初始化任务表
*/
initTaskDB(context) {
// 1. rdb 配置
const config = {
name: 'MyApplication.db',
securityLevel: relationalStore.SecurityLevel.S1
}
// 2. 初始化SQL语句
const sql = `CREATE TABLE IF NOT EXISTS ${this.tableName}(
ID INTEGER PRIMARY KEY AUTOINCREMENT,
NAME TEXT NOT NULL,
FINISHED bit
)`
// 3. 获取rdb
relationalStore.getRdbStore(context, config, (err, rdbStore) => {
if(err) {
console.log('testTag', '获取rdbStore失败')
return
}
// 执行sql
rdbStore.executeSql(sql)
console.log('testTag', '创建task表成功')
this.rdbStore = rdbStore
})
}
/**
* 查询任务列表
*/
async getTaskList() {
// 1. 构建查询条件
let predicates = new relationalStore.RdbPredicates(this.tableName)
// 2. 查询
let result = await this.rdbStore.query(predicates, ['ID', 'NAME', 'FINISHED'])
// 3. 解析查询结果
// 3.1 定义一个数组,组装最终结果
let tasks: TaskInfo[] = []
// 3.2 封装
while(!result.isAtLastRow) {
// 3.3 指针移动到下一行
result.goToNextRow()
// 3.4 获取数据
let id = result.getLong(result.getColumnIndex('ID'))
let name = result.getString(result.getColumnIndex('NAME'))
let finished = result.getLong(result.getColumnIndex('FINISHED'))
// 3.5 封装到数组
tasks.push({id, name, finishFlag: !!finished})
}
console.log('testTag', '查询到数据', JSON.stringify(tasks))
return tasks
}
/**
* 添加一个新任务
*/
addTask(name: string): Promise<number> {
return this.rdbStore.insert(this.tableName, {name, finishFlag: false})
}
/**
* 更新任务
*/
updateTaskStatus(id: number, finished: boolean) {
// 1. 要更新的数据
let data = {finishFlag: finished}
// 2. 更新的条件
let predicates = new relationalStore.RdbPredicates(this.tableName)
predicates.equalTo('ID', id)
// 3. 更新操作
return this.rdbStore.update(data, predicates)
}
/**
* 删除一个任务
*/
deleteTaskById(id: number) {
// 1. 删除的条件
let predicates = new relationalStore.RdbPredicates(this.tableName)
predicates.equalTo("ID", id)
// 2. 删除操作
return this.rdbStore.delete(predicates)
}
}
let taskModel = new TaskModel();
export default TaskModel在 entryAbility 中进行初始化操作
async onCreate(want, launchParam) {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
// 加载Preferences
await PreferencesUtil.loadPreference(this.context, 'MyPreferences')
taskModel.initTaskDB(this.context)
}通知
消息通知
应用可以通过通知接口发送通知消息,提醒用户关注应用中的变化,用户可以在通知栏查看和操作通知内容。
用法:

通知四种类型:

普通文本通知:

长文本通知:

多行文本通知:

图片通知:

其他的一些属性:

进度条通知
进度条通知会展示一个动态的进度条,主要用于文件下载、长任务处理的进度显示

通知意图
我们可以给通知添加或其中的按钮设置行为意图(Want),从而实现拉起应用组件或发布公共事件等能力。
其实就是当用户点击通知时,拉起我们的应用
用法:

ArkTs中组件
Image
通过网络请求获取数据
Image('https://www4.bing.com//th?id=OHR.RegistanUzbekistan_ZH-CN7850329702_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp')
.width(250)
.borderRadius(10)
// ImageInterpolation.High 处理图片虚焦、让图片更加圆滑
.interpolation(ImageInterpolation.High)通过
$r获取本地 resources/base/media 下的图片内容
Image($r('app.media.mate60'))
.width(250)
.borderRadius(10)
.interpolation(ImageInterpolation.High)通过
$rawfile获取本地 resources/rawfile 下的图片内容
Image($rawfile('mate60.png'))
.width(250)
.borderRadius(10)
.interpolation(ImageInterpolation.High)List
作用:当内容信息超出高度时,自动添加滚动条,可进行滚动,相当于 React Native 中的 ScrollView
⚠注意:该组件只能拥有一个父组件
用法:
List({ space: 8 }) {
ForEach(
arr,
(item, index) => {
},
)
}
// 相当于 Flex 属性 ,当其中传入 1 时,会自动平分余下空间
// 传入 2 时,将会占两份
.layoutWeight(1)
// 当触底的时候,将会调用此函数
.onReachEnd(() => {
console.log("触底了 ")
})
Blank
作用:当我们想要实现左右布局时,需要在左元素和右元素之间添加空白,直接使用此元素即可
添加前:

添加后:

用法:
Row() {
Image($r('app.media.back'))
.width(30)
Text("商品列表")
.fontSize(30)
.fontWeight(FontWeight.Bold)
Blank()
Image($r('app.media.refresh'))
.width(30)
}Stack
作用:堆叠容器,子组件按照顺序依次入栈,后一个子组件覆盖前一个子组件。
使用前:

使用后:

用法:
Stack() {
Progress({
value: this.finishTask,
total: this.totalTask,
type: ProgressType.Ring
})
.width(100)
Text(`${this.finishTask} / ${this.totalTask}`)
.fontSize(30)
}弹窗组件
新建一个弹窗组件,页面中组件写在 ets/view文件夹下,这里写一个
// 对于没有入口的组件类型的,添加@preview即可预览
import { CommonConstants } from '../../common/constants/CommonConstants'
@CustomDialog
export default struct UserPrivacyDialog {
controller: CustomDialogController
// 点击确定时调用
confirm: () => void
// 点击取消时调用
cancel: () => void
build() {
Column({space: CommonConstants.SPACE_10}) {
// 1. 标题
Text($r('app.string.user_privacy_title'))
.fontSize(20)
.fontWeight(CommonConstants.FONT_WEIGHT_700)
// 2. 内容
Text($r('app.string.user_privacy_content'))
.fontSize(16)
// 3. 按钮
Button($r('app.string.agree_label'))
.width(150)
.backgroundColor($r('app.color.primary_color'))
.onClick(() => {
this.confirm()
this.controller.close()
})
Button($r('app.string.refuse_label'))
.width(150)
.backgroundColor($r('app.color.lightest_primary_color'))
.fontColor($r('app.color.light_gray'))
.onClick(() => {
this.cancel()
this.controller.close()
})
}
.width('100%')
.padding(10)
}
}添加
@Preivew装饰器即可进行预览
在需要使用的页面组件中调用
import UserPrivacyDialog from '../view/welcome/UserPrivacyDialog'
@Entry
@Component
struct WelcomePage {
controller: CustomDialogController = new CustomDialogController({
builder: UserPrivacyDialog({
confirm: () => this.onConfirm(),
cancel: () => this.exitApp()
})
})
aboutToAppear() {
this.controller.open()
// 也可以进行关闭
this.controller.close()
}
}Tabbar
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Tabs({barPosition: BarPosition.End}) {
TabContent() {
Text('内容1')
}
.tabBar('标题1')
TabContent() {
Text('内容2')
}
.tabBar('标题2')
}
}
}

ArkTs中的杂项
退出App
import common from '@ohos.app.ability.common'
exitApp() {
// 退出App
this.context.terminateSelf()
}