HarmonyOS通过公共事件服务为应用程序提供订阅、发布、退订公共事件的能力。
5.1 公共事件概述
在应用里面,往往会有事件。比如,朋友给我手机发了一条信息,未读信息会在手机的通知栏给出提示。
5.1.1 公共事件的分类
公共事件(Common Event Service,CES)根据事件发送方不同,可分为系统公共事件和自定义公共事件,如图所示。
1.系统公共事件:系统将收集到的事件信息根据系统策略发送给订阅该事件的用户程序。 公共事件包括终端设备用户可感知的亮灭屏事件,以及系统关键服务发布的系统事件(例如USB插拔、网络连接、系统升级)等。
2.自定义公共事件:由应用自身定义的期望特定订阅者可以接收到的公共事件,这些公共事件往往与应用自身的业务逻辑相关。
每个应用都可以按需订阅公共事件,订阅成功且公共事件发布,系统会把其发送和公共事件取消订阅开发。
5.1.2 公共事件的开发
公共事件的开发主要涉及3部分,即公共事件订阅开发、公共事件发布开发和公共事件取消订阅开发。
1.公共事件订阅开发
当需要订阅某个公共事件,获取某个公共事件传递的参数时,可以创建一个订阅者对象,用于作为订阅公共事件的载体,订阅公共事件并获取公共事件传递而来的参数。 发送粘性事件必须是系统应用或系统服务, 且需要申请ohos.permission.COMMONEVENT_STICKY权限 。 公共事件订阅开发的接口如下:
创建订阅者对象(callback):createSubscriber(subscribeInfo: CommonEventSubscribeInfo, callback: AsyncCallback)。 创建订阅者对象(promise):createSubscriber(subscribeInfo: CommonEventSubscribeInfo)。 订阅公共事件:subscribe(subscriber: CommonEventSubscriber, callback: AsyncCallback)。
2.公共事件发布开发
当需要发布某个自定义公共事件时,可以通过此方法发布事件。发布的公共事件可以携带数据,供订阅者解析并进行下一步处理。 公共事件发布开发的接口如下:
发布公共事件:publish(event: string, callback: AsyncCallback)。 指定发布信息并发布公共事件:publish(event: string, options: CommonEventPublishData, callback: AsyncCallback)。
3.公共事件取消订阅开发
订阅者需要取消已订阅的某个公共事件时,可以通过此方法取消订阅事件。 公共事件取消订阅开发的接口如下:
取消订阅公共事件:unsubscribe(subscriber: CommonEventSubscriber, callback?: AsyncCallback)。
5.2 订阅、发布、取消公共事件
打开DevEco Studio,选择一个Empty Ability工程模板,创建一个名为ArkTSCommonEventService的工程为演示示例。
5.2.1 添加按钮
在Index.ets的Text组件下,添加4个按钮,代码如下:
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
//创建订阅者
Button(('创建订阅者'), { type: ButtonType.Capsule })
.fontSize(40)
.fontWeight(FontWeight.Medium)
.margin({ top: 10, bottom: 10 })
.onClick(() => {
//创建订阅者
// this.createSubscriber()
})
//订阅事件
Button(('订阅事件'), { type: ButtonType.Capsule })
.fontSize(40)
.fontWeight(FontWeight.Medium)
.margin({ top: 10, bottom: 10 })
.onClick(() => {
//订阅事件
// this.subscriberCommonEvent()
})
//发送事件
Button(('发送事件'), { type: ButtonType.Capsule })
.fontSize(40)
.fontWeight(FontWeight.Medium)
.margin({ top: 10, bottom: 10 })
.onClick(() => {
//发送事件
// this.publishCommonEvent()
})
//取消订阅事件
Button(('取消订阅'), { type: ButtonType.Capsule })
.fontSize(40)
.fontWeight(FontWeight.Medium)
.margin({ top: 10, bottom: 10 })
.onClick(() => {
//取消订阅事件
// this.unsubscribeCommonEvent()
})
}
.width('100%')
}
.height('100%')
}
}
5.2.2 添加Text显示接收的事件
为了能显示接收到的事件的信息,在4个按钮下添加一个Text组件,代码如下:
//用于接收事件数据
@State eventData: string = ''
//接收到的事件数据
Text(this.eventData)
.fontSize(50)
.fontWeight(FontWeight.Bold)
Text组件的显示内容通过@State绑定了eventData变量。当eventData变量变化时,Text的显示内容也会实时更新。
5.2.3 设置按钮的单击事件方法
4个按钮的单击事件方法如下:
//用于保存创建成功的订阅者对象,后续使用其完成订阅及退订的动作
private subscriber = null
//导入commonEvent模块
import commonEvent from '@ohos.commonEvent';
private createSubscriber() {
if (this.subscriber) {
this.message = "subscriber already created";
} else {
commonEvent.createSubscriber({ //创建订阅者
events: ["testEvent"] //指定订阅的事件名称
}, (err, subscriber) => { //创建结果的回调
if (err) {
this.message = "create subscriber failure"
} else {
this.subscriber = subscriber; //创建订阅成功
this.message = "create subscriber success";
}
})
}
}
在第一个按钮的点击事件中调用该方法 代码如下:this.createSubscriber()
点击第一个按钮,看到如下效果:
添加订阅事件的方法:
private subscriberCommonEvent() {
if (this.subscriber) {
//根据创建的subscriber开始订阅事件
commonEvent.subscribe(this.subscriber, (err, data) => {
if (err) {
//异常处理
this.eventData = "subscribe event failure: " + err;
} else {
//接收到事件
this.eventData = "subscribe event success: " + JSON.stringify(data);
}
})
} else {
this.message = "please create subscriber";
}
}
订阅事件前先判断是否创建订阅事件。在第二个按钮中调用该方法,代码如下:this.subscriberCommonEvent()
点击第二个按钮,看到如下效果:
发送事件:
private publishCommonEvent() {
//发布公共事件
commonEvent.publish("testEvent", (err) => { //结果回调
if (err) {
this.message = "publish event error: " + err;
} else {
this.message = "publish event with data success";
}
})
}
在第三个按钮中添加调用该方法的代码:this.publishCommonEvent()
点击第三个按钮,效果如下:
取消订阅的代码如下:
private unsubscribeCommonEvent() {
if (this.subscriber) {
commonEvent.unsubscribe(this.subscriber, (err) => { //取消订阅事件
if (err) {
this.message = "unsubscribe event failure: " + err;
} else {
this.subscriber = null;
this.message = "unsubscribe event success";
}
})
} else {
this.message = "already subscribed";
}
}
在第四个按钮中添加调用代码:this.unsubscribeCommonEvent()
点击第四个按钮,效果如下:
5.3 支持交互事件
5.3.1 交互事件概述
通用事件按照触发类型来分类,包括触屏事件、键鼠事件和焦点事件。
-
触屏事件:手指或手写笔在触屏上的单指或单笔操作。
-
键鼠事件
:包括外设鼠标或触控板的操作事件和外设键盘的按键事件。
-
鼠标事件是指通过连接和使用外设鼠标/触控板操作时所响应的事件。
-
按键事件是指通过连接和使用外设键盘操作时所响应的事件。
-
-
焦点事件:通过以上方式控制组件焦点的能力和响应的事件。
手势事件由绑定手势方法和绑定的手势组成,绑定的手势可以分为单一手势和组合手势两种类型,根据手势的复杂程度进行区分。
-
绑定手势方法:用于在组件上绑定单一手势或组合手势,并声明所绑定的手势的响应优先级。
-
单一手势:手势的基本单元,是所有复杂手势的组成部分。
-
组合手势:由多个单一手势组合而成,可以根据声明的类型将多个单一手势按照一定规则组合成组合手势,并进行使用。
5.3.2 使用通用事件
1.触屏事件
触屏事件指当手指/手写笔在组件上按下、滑动、抬起时触发的回调事件。包括点击事件、拖拽事件和触摸事件。
点击事件
点击事件是指通过手指或手写笔做出一次完整的按下和抬起动作。当发生点击事件时,会触发以下回调函数:
onClick(event: (event?: ClickEvent) => void)
event参数提供点击事件相对于窗口或组件的坐标位置,以及发生点击的事件源。
例如通过按钮的点击事件控制图片的显示和隐藏。
@Entry
@Component
struct A1 {
@State flag: boolean = true;
@State btnMsg: string = 'show';
build() {
Column() {
Button(this.btnMsg).width(80).height(30).margin(30)
.onClick(() => {
if (this.flag) {
this.btnMsg = 'hide';
} else {
this.btnMsg = 'show';
}
// 点击Button控制Image的显示和消失
this.flag = !this.flag;
})
if (this.flag) {
Image($r('app.media.icon')).width(200).height(200)
}
}.height('100%').width('100%')
}
}
根据代码,自行查看点击效果,该点击事件是最常用的事件。
拖拽事件
拖拽事件指手指/手写笔长按组件(>=500ms),并拖拽到接收区域释放的事件。拖拽事件触发流程:
拖拽事件的触发通过长按、拖动平移判定,手指平移的距离达到5vp即可触发拖拽事件。ArkUI支持应用内、跨应用的拖拽事件。
拖拽事件提供以下接口:
接口名称 | 描述 |
---|---|
onDragStart(event: (event?: DragEvent, extraParams?: string) => CustomBuilder | DragItemInfo) | 拖拽启动接口。当前仅支持自定义pixelmap和自定义组件。 |
onDragEnter(event: (event?: DragEvent, extraParams?: string) => void) | 拖拽进入组件接口。DragEvent定义拖拽发生位置,extraParmas表示用户自定义信息 |
onDragLeave(event: (event?: DragEvent, extraParams?: string) => void) | 拖拽离开组件接口。DragEvent定义拖拽发生位置,extraParmas表示拖拽事件额外信息。 |
onDragMove(event: (event?: DragEvent, extraParams?: string) => void) | 拖拽移动接口。DragEvent定义拖拽发生位置,extraParmas表示拖拽事件额外信息。 |
onDrop(event: (event?: DragEvent, extraParams?: string) => void) | 拖拽释放组件接口。DragEvent定义拖拽发生位置,extraParmas表示拖拽事件额外信息。 |
触摸事件
当手指或手写笔在组件上触碰时,会触发不同动作所对应的事件响应,包括按下(Down)、滑动(Move)、抬起(Up)事件:
onTouch(event: (event?: TouchEvent) => void)
-
event.type为TouchType.Down:表示手指按下。
-
event.type为TouchType.Up:表示手指抬起。
-
event.type为TouchType.Move:表示手指按住移动。
触摸事件可以同时多指触发,通过event参数可获取触发的手指位置、手指唯一标志、当前发生变化的手指和输入的设备源等信息。
2.键鼠事件
键鼠事件指键盘,鼠标外接设备的输入事件。
鼠标事件
支持的鼠标事件包含通过外设鼠标、触控板触发的事件。
鼠标事件可触发以下回调:
名称 | 描述 |
---|---|
onHover(event: (isHover: boolean) => void) | 鼠标进入或退出组件时触发该回调。isHover:表示鼠标是否悬浮在组件上,鼠标进入时为true, 退出时为false。 |
onMouse(event: (event?: MouseEvent) => void) | 当前组件被鼠标按键点击时或者鼠标在组件上悬浮移动时,触发该回调,event返回值包含触发事件时的时间戳、鼠标按键、动作、鼠标位置在整个屏幕上的坐标和相对于当前组件的坐标。 |
当组件绑定onHover回调时,可以通过hoverEffect属性设置该组件的鼠标悬浮态显示效果。
鼠标事件传递到ArkUI之后,会先判断鼠标事件是否是左键的按下/抬起/移动,然后做出不同响应:
-
是:鼠标事件先转换成相同位置的触摸事件,执行触摸事件的碰撞测试、手势判断和回调响应。接着去执行鼠标事件的碰撞测试和回调响应。
-
否:事件仅用于执行鼠标事件的碰撞测试和回调响应。
说明
所有单指可响应的触摸事件/手势事件,均可通过鼠标左键来操作和响应。例如当我们需要开发单击Button跳转页面的功能、且需要支持手指点击和鼠标左键点击,那么只绑定一个点击事件(onClick)就可以实现该效果。若需要针对手指和鼠标左键的点击实现不一样的效果,可以在onClick回调中,使用回调参数中的source字段即可判断出当前触发事件的来源是手指还是鼠标。
onHover(event: (isHover?: boolean) => void)
鼠标悬浮事件回调。参数isHover类型为boolean,表示鼠标进入组件或离开组件。该事件不支持自定义冒泡设置,默认父子冒泡。
若组件绑定了该接口,当鼠标指针从组件外部进入到该组件的瞬间会触发事件回调,参数isHover等于true;鼠标指针离开组件的瞬间也会触发该事件回调,参数isHover等于false。
说明
事件冒泡:在一个树形结构中,当子节点处理完一个事件后,再将该事件交给它的父节点处理。
@Entry
@Component
struct A2 {
@State isHovered: boolean = false;
build() {
Column() {
Button(this.isHovered ? 'Hovered!' : 'Not Hover')
.width(200).height(100)
.backgroundColor(this.isHovered ? Color.Green : Color.Gray)
.onHover((isHover: boolean) => { // 使用onHover接口监听鼠标是否悬浮在Button组件上
this.isHovered = isHover;
})
}.width('100%').height('100%').justifyContent(FlexAlign.Center)
}
}
该示例创建了一个Button组件,初始背景色为灰色,内容为“Not Hover”。示例中的Button组件绑定了onHover回调,在该回调中将this.isHovered变量置为回调参数:isHover。
当鼠标从Button外移动到Button内的瞬间,回调响应,isHover值等于true,isHovered的值变为true,将组件的背景色改成Color.Green,内容变为“Hovered!”。
当鼠标从Button内移动到Button外的瞬间,回调响应,isHover值等于false,又将组件变成了初始的样式。
onMouse
onMouse(event: (event?: MouseEvent) => void)
鼠标事件回调。绑定该API的组件每当鼠标指针在该组件内产生行为(MouseAction)时,触发事件回调,参数为MouseEvent对象,表示触发此次的鼠标事件。该事件支持自定义冒泡设置,默认父子冒泡。常见用于开发者自定义的鼠标行为逻辑处理。
开发者可以通过回调中的MouseEvent对象获取触发事件的坐标(screenX/screenY/x/y)、按键(MouseButton)、行为(MouseAction)、时间戳(timestamp)、交互组件的区域(EventTarget)、事件来源(SourceType)等。MouseEvent的回调函数stopPropagation用于设置当前事件是否阻止冒泡。
onKeyEvent
onKeyEvent(event: (event?: KeyEvent) => void)
按键事件回调,当绑定该方法的组件处于获焦状态下,外设键盘的按键事件会触发该API的回调响应,回调参数为KeyEvent,可由该参数获得当前按键事件的按键行为(KeyType)、键码(keyCode)、按键英文名称(keyText)、事件来源设备类型(KeySource)、事件来源设备id(deviceId)、元键按压状态(metaKey)、时间戳(timestamp)、阻止冒泡设置(stopPropagation)。
@Entry
@Component
struct A2 {
@State buttonText: string = '';
@State buttonType: string = '';
@State columnText: string = '';
@State columnType: string = '';
build() {
Column() {
Button('onKeyEvent')
.width(140).height(70)
.onKeyEvent((event: KeyEvent) => { // 给Button设置onKeyEvent事件
if (event.type === KeyType.Down) {
this.buttonType = 'Down';
}
if (event.type === KeyType.Up) {
this.buttonType = 'Up';
}
this.buttonText = 'Button: \n' +
'KeyType:' + this.buttonType + '\n' +
'KeyCode:' + event.keyCode + '\n' +
'KeyText:' + event.keyText;
})
Divider()
Text(this.buttonText).fontColor(Color.Green)
Divider()
Text(this.columnText).fontColor(Color.Red)
}.width('100%').height('100%').justifyContent(FlexAlign.Center)
.onKeyEvent((event: KeyEvent) => { // 给父组件Column设置onKeyEvent事件
if (event.type === KeyType.Down) {
this.columnType = 'Down';
}
if (event.type === KeyType.Up) {
this.columnType = 'Up';
}
this.columnText = 'Column: \n' +
'KeyType:' + this.buttonType + '\n' +
'KeyCode:' + event.keyCode + '\n' +
'KeyText:' + event.keyText;
})
}
}
打开应用后,依次在键盘上按这些按键:“空格、回车、左Ctrl、左Shift、字母A、字母Z”。
-
由于onKeyEvent事件默认是冒泡的,所以Button和Column的onKeyEvent都可以响应。
-
每个按键都有2次回调,分别对应KeyType.Down和KeyType.Up,表示按键被按下、然后抬起。
如果要阻止冒泡,即仅Button响应键盘事件,Column不响应,在Button的onKeyEvent回调中加入event.stopPropagation()方法即可,如下:
Button('onKeyEvent')
.width(140).height(70)
.onKeyEvent((event: KeyEvent) => {
// 通过stopPropagation阻止事件冒泡
event.stopPropagation();
if (event.type === KeyType.Down) {
this.buttonType = 'Down';
}
if (event.type === KeyType.Up) {
this.buttonType = 'Up';
}
this.buttonText = 'Button: \n' +
'KeyType:' + this.buttonType + '\n' +
'KeyCode:' + event.keyCode + '\n' +
'KeyText:' + event.keyText;
})
5.3 小结
本章介绍了在HarmonyOS中的公共事件的概念以及用法,并演示了公共事件的订阅、发布和取消操作。这里只介绍了部分事件,如果想要学习更多的事件,可以进入官网查看API文档进行学习。
5.4 习题
1.判断题
(1)事件方法:用于添加组件对事件的响应逻辑,统一通过事件方法进行设置,如跟随在Button后面的onClick()。( )
(2)公共事件的开发主要涉及3部分,即公共事件订阅开发、公共事件发布开发和公共事件取消订阅开发。( )
2.多选题
公共事件(Common Event Service,CES)根据事件发送方不同可分为哪几类?( )
A. 系统公共事件 B. 自定义公共事件