提交 fff61b3b 编写于 作者: Z zengyawen

update docs

Signed-off-by: Nzengyawen <zengyawen1@huawei.com>
上级 59470837
# UI开发
- [方舟开发框架(ArkUI)概述](arkui-overview.md)
- 基于ArkTS的声明式开发范式
- [概述](ui-ts-overview.md)
- [声明式UI开发指导](ui-ts-developing-intro.md)
- 声明式UI开发实例
- [创建简单视图](ui-ts-creating-simple-page.md)
- 构建完整实例
- [构建食物数据模型](ui-ts-building-data-model.md)
- [构建食物列表List布局](ui-ts-building-category-list-layout.md)
- [构建食物分类Grid布局](ui-ts-building-category-grid-layout.md)
- [页面跳转与数据传递](ui-ts-page-redirection-data-transmission.md)
- 添加闪屏动画
- [绘制图像](ui-ts-drawing-feature.md)
- [添加动画效果](ui-ts-animation-feature.md)
- [常用组件说明](ui-ts-components-intro.md)
- 常见布局开发指导
- 自适应布局
- [线性布局](ui-ts-layout-linear.md)
- [层叠布局](ui-ts-layout-stack.md)
- [弹性布局](ui-ts-layout-flex.md)
- [网格布局](ui-ts-layout-grid.md)
- 响应式布局
- [栅格布局](ui-ts-layout-grid-container-new.md)
- [媒体查询](ui-ts-layout-mediaquery.md)
- [自定义组件的生命周期](ui-ts-custom-component-lifecycle-callbacks.md)
- [Web组件开发指导](ui-ts-components-web.md)
- [性能提升的推荐方法](ui-ts-performance-improvement-recommendation.md)
- 兼容JS的类Web开发范式
- [概述](ui-js-overview.md)
- [方舟开发框架概述](arkui-overview.md)
- UI开发(ArkTS声明式开发范式)
- [UI开发(ArkTS声明式开发范式)概述](arkts-ui-development-overview.md)
- 开发布局
- [布局概述](arkts-layout-development-overview.md)
- 构建布局
- [线性布局](arkts-layout-development-linear.md)
- [层叠布局](arkts-layout-development-stack-layout.md)
- [弹性布局](arkts-layout-development-flex-layout.md)
- [相对布局](arkts-layout-development-relative-layout.md)
- [栅格布局](arkts-layout-development-grid-layout.md)
- [媒体查询](arkts-layout-development-media-query.md)
- [创建列表](arkts-layout-development-create-list.md)
- [创建网格](arkts-layout-development-create-grid.md)
- [创建轮播](arkts-layout-development-create-looping.md)
- [改善布局性能](arkts-layout-development-performance-boost.md)
- 添加组件
- 添加常用组件
- [按钮](arkts-common-components-button.md)
- [单选框](arkts-common-components-radio-button.md)
- [切换按钮](arkts-common-components-switch.md)
- [进度条](arkts-common-components-progress-indicator.md)
- [文本显示](arkts-common-components-text-display.md)
- [文本输入](arkts-common-components-text-input.md)
- [自定义弹窗](arkts-common-components-custom-dialog.md)
- [视频播放](arkts-common-components-video-player.md)
- [XComponent](arkts-common-components-xcomponent.md)
- 添加气泡和菜单
- [气泡提示](arkts-popup-and-menu-components-popup.md)
- [菜单](arkts-popup-and-menu-components-menu.md)
- 设置页面路由和组件导航
- [页面路由](arkts-routing.md)
- 组件导航
- [Navigation](arkts-navigation-navigation.md)
- [Tabs](arkts-navigation-tabs.md)
- 显示图形
- [显示图片](arkts-graphics-display.md)
- [绘制几何图形](arkts-geometric-shape-drawing.md)
- [使用画布绘制自定义图形](arkts-drawing-customization-on-canvas.md)
- 使用动画
- [动画概述](arkts-animation-overview.md)
- 页面内的动画
- [布局更新动画](arkts-layout-update-animation.md)
- [组件内转场动画](arkts-transition-animation-within-component.md)
- [弹簧曲线动画](arkts-spring-animation.md)
- 页面间的动画
- [放大缩小视图](arkts-zoom-animation.md)
- [页面转场动画](arkts-page-transition-animation.md)
- 支持交互事件
- [交互事件概述](arkts-event-overview.md)
- 使用通用事件
- [触屏事件](arkts-common-events-touch-screen-event.md)
- [键鼠事件](arkts-common-events-device-input-event.md)
- [焦点事件(毕雪峰 00579046)](arkts-common-events-focus-event.md)
- 使用手势事件
- [绑定手势方法](arkts-gesture-events-binding.md)
- [单一手势](arkts-gesture-events-single-gesture.md)
- [组合手势](arkts-gesture-events-combined-gestures.md)
- [性能提升的推荐方法](arkts-performance-improvement-recommendation.md)
- UI开发(兼容JS的类Web开发范式)
- [UI开发(兼容JS的类Web开发范式)概述](ui-js-overview.md)
- 框架说明
- [文件组织](js-framework-file.md)
- [js标签配置](js-framework-js-tag.md)
......
# 动画概述
动画的原理是在一个时间段内,多次改变UI外观,由于人眼会产生视觉暂留,所以最终看到的就是一个“连续”的动画。UI的一次改变称为一个动画帧,对应一次屏幕刷新,而决定动画流畅度的一个重要指标就是帧率FPS(Frame Per Second),即每秒的动画帧数,帧率越高则动画就会越流畅。
ArkUI中,产生动画的方式是改变属性值且指定动画参数。动画参数包含了如动画时长、变化规律(即曲线)等参数。当属性值发生变化后,按照动画参数,从原来的状态过渡到新的状态,即形成一个动画。
ArkUI提供的动画能力按照页面的分类方式,可分为页面内的动画和页面间的动画。如下图所示,页面内的动画指在一个页面内即可发生的动画,页面间的动画指两个页面跳转时才会发生的动画。
**图1** 按照页面分类的动画  
![zh-cn_image_0000001562700385](figures/zh-cn_image_0000001562700385.png)
如果按照基础能力分,可分为属性动画、显式动画、转场动画三部分。如下图所示。
**图2** 按照基础能力分类的动画  
![zh-cn_image_0000001562820753](figures/zh-cn_image_0000001562820753.png)
本文按照页面的分类方式,从使用场景出发,提供各种动画的使用方法和注意事项,使开发者快速学习动画。
# 按钮
Button是按钮组件,通常用于响应用户的点击操作,其类型包括胶囊按钮、圆形按钮、普通按钮。Button当做为容器使用时可以通过添加子组件实现包含文字、图片等元素的按钮。具体用法请参考[Button](../reference/arkui-ts/ts-basic-components-button.md)
## 创建按钮
Button通过调用接口来创建,接口调用有以下两种形式:
- 创建不包含子组件的按钮。
```ts
Button(label?: string, options?: { type?: ButtonType, stateEffect?: boolean })
```
该接口用于创建不包含子组件的按钮,其中label用来设置按钮文字,type用于设置Button类型,stateEffect属性设置Button是否开启点击效果。
```ts
Button('Ok', { type: ButtonType.Normal, stateEffect: true })
.borderRadius(8)
.backgroundColor(0x317aff)
.width(90)
.height(40)
```
![zh-cn_image_0000001562820757](figures/zh-cn_image_0000001562820757.png)
- 创建包含子组件的按钮。
```ts
Button(options?: {type?: ButtonType, stateEffect?: boolean})
```
该接口用于创建包含子组件的按钮,只支持包含一个子组件,子组件可以是[基础组件](../reference/arkui-ts/ts-basic-components-blank.md)或者[容器组件](../reference/arkui-ts/ts-container-ability-component.md)
```ts
Button({ type: ButtonType.Normal, stateEffect: true }) {
Row() {
Image($r('app.media.loading')).width(20).height(40).margin({ left: 12 })
Text('loading').fontSize(12).fontColor(0xffffff).margin({ left: 5, right: 12 })
}.alignItems(VerticalAlign.Center)
}.borderRadius(8).backgroundColor(0x317aff).width(90).height(40)
```
![zh-cn_image_0000001511421216](figures/zh-cn_image_0000001511421216.png)
## 设置按钮类型
Button有三种可选类型,分别为Capsule(胶囊类型)、Circle(圆形按钮)和Normal(普通按钮),通过type进行设置。
- 胶囊按钮(默认类型)
此类型按钮的圆角自动设置为高度的一半,不支持通过borderRadius属性重新设置圆角。
```ts
Button('Disable', { type: ButtonType.Capsule, stateEffect: false })
.backgroundColor(0x317aff)
.width(90)
.height(40)
```
![zh-cn_image_0000001511421208](figures/zh-cn_image_0000001511421208.png)
- 圆形按钮
此类型按钮为圆形,不支持通过borderRadius属性重新设置圆角。
```ts
Button('Circle', { type: ButtonType.Circle, stateEffect: false })
.backgroundColor(0x317aff)
.width(90)
.height(90)
```
![zh-cn_image_0000001511740428](figures/zh-cn_image_0000001511740428.png)
- 普通按钮
此类型的按钮默认圆角为0,支持通过borderRadius属性重新设置圆角。
```ts
Button('Ok', { type: ButtonType.Normal, stateEffect: true })
.borderRadius(8)
.backgroundColor(0x317aff)
.width(90)
.height(40)
```
![zh-cn_image_0000001563060641](figures/zh-cn_image_0000001563060641.png)
## 自定义样式
- 设置边框弧度。
一般使用通用属性来自定义按钮样式。例如通过borderRadius属性设置按钮的边框弧度。
```ts
Button('circle border', { type: ButtonType.Normal })
.borderRadius(20)
.height(40)
```
![zh-cn_image_0000001511900392](figures/zh-cn_image_0000001511900392.png)
- 设置文本样式。
通过添加文本样式设置按钮文本的展示样式。
```ts
Button('font style', { type: ButtonType.Normal })
.fontSize(20)
.fontColor(Color.Pink)
.fontWeight(800)
```
![zh-cn_image_0000001511580828](figures/zh-cn_image_0000001511580828.png)
- 设置背景颜色。
添加backgroundColor属性设置按钮的背景颜色。
```ts
Button('background color').backgroundColor(0xF55A42)
```
![zh-cn_image_0000001562940477](figures/zh-cn_image_0000001562940477.png)
- 用作功能型按钮。
为删除操作创建一个按钮。
```ts
Button({ type: ButtonType.Circle, stateEffect: true }) {
Image($r('app.media.ic_public_delete_filled')).width(30).height(30)
}.width(55).height(55).margin({ left: 20 }).backgroundColor(0xF55A42)
```
![zh-cn_image_0000001511740436](figures/zh-cn_image_0000001511740436.png)
## 添加事件
Button组件通常用于触发某些操作,可以绑定onClick事件来响应点击操作后的自定义行为。
```ts
Button('Ok', { type: ButtonType.Normal, stateEffect: true })
.onClick(()=>{
console.info('Button onClick')
})
```
## 场景示例
- 用于启动操作。
可以用按钮启动任何用户界面元素,按钮会根据用户的操作触发相应的事件。例如,在List容器里通过点击按钮进行页面跳转。
```ts
// xxx.ets
import router from '@ohos.router';
@Entry
@Component
struct ButtonCase1 {
build() {
List({ space: 4 }) {
ListItem() {
Button("First").onClick(() => {
router.pushUrl({ url: 'pages/first_page' })
})
.width('100%')
}
ListItem() {
Button("Second").onClick(() => {
router.pushUrl({ url: 'pages/second_page' })
})
.width('100%')
}
ListItem() {
Button("Third").onClick(() => {
router.pushUrl({ url: 'pages/third_page' })
})
.width('100%')
}
}
.listDirection(Axis.Vertical)
.backgroundColor(0xDCDCDC).padding(20)
}
}
```
![zh-cn_image_0000001562700393](figures/zh-cn_image_0000001562700393.png)
- 用于表单的提交。
在用户登录/注册页面,使用按钮进行登录或注册操作。
```ts
// xxx.ets
@Entry
@Component
struct ButtonCase2 {
build() {
Column() {
TextInput({ placeholder: 'input your username' }).margin({ top: 20 })
TextInput({ placeholder: 'input your password' }).type(InputType.Password).margin({ top: 20 })
Button('Register').width(300).margin({ top: 20 })
.onClick(() => {
// 需要执行的操作
})
}.padding(20)
}
}
```
![zh-cn_image_0000001562940473](figures/zh-cn_image_0000001562940473.png)
- 悬浮按钮
在可以滑动的界面,滑动时按钮始终保持悬浮状态。
```ts
// xxx.ets
@Entry
@Component
struct HoverButtonExample {
private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
build() {
Stack() {
List({ space: 20, initialIndex: 0 }) {
ForEach(this.arr, (item) => {
ListItem() {
Text('' + item)
.width('100%').height(100).fontSize(16)
.textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF)
}
}, item => item)
}.width('90%')
Button() {
Image($r('app.media.ic_public_add'))
.width(50)
.height(50)
}
.width(60)
.height(60)
.position({x: '80%', y: 600})
.shadow({radius: 10})
.onClick(() => {
// 需要执行的操作
})
}
.width('100%')
.height('100%')
.backgroundColor(0xDCDCDC)
.padding({ top: 5 })
}
}
```
![GIF](figures/GIF.gif)
# 自定义弹窗
自定义弹窗CustomDialog可用于广告、中奖、警告、软件更新等与用户交互响应操作。开发者可以通过CustomDialogController类显示自定义弹窗。具体用法请参考[自定义弹窗](../reference/arkui-ts/ts-methods-custom-dialog-box.md)
## 创建自定义弹窗
1. 使用\@CustomDialog装饰器装饰自定义弹窗。
2. \@CustomDialog装饰器用于装饰自定义弹框,此装饰器内进行自定义内容(也就是弹框内容)。
```ts
@CustomDialog
struct CustomDialogExample {
controller: CustomDialogController
build() {
Column() {
Text('我是内容')
.fontSize(20)
.margin({ top: 10, bottom: 10 })
}
}
}
```
3. 创建构造器,与装饰器呼应相连。
```ts
dialogController: CustomDialogController = new CustomDialogController({
builder: CustomDialogExample({}),
})
```
4. 点击与onClick事件绑定的组件使弹窗弹出
```ts
Flex({justifyContent:FlexAlign.Center}){
Button('click me')
.onClick(() => {
this.dialogController.open()
})
}.width('100%')
```
![zh-cn_image_0000001562700493](figures/zh-cn_image_0000001562700493.png)
## 弹窗的交互
弹窗可用于数据交互,完成用户一系列响应操作。
1.\@CustomDialog装饰器内添加按钮操作,同时添加数据函数的创建。
```ts
@CustomDialog
struct CustomDialogExample {
controller: CustomDialogController
cancel: () => void
confirm: () => void
build() {
Column() {
Text('我是内容').fontSize(20).margin({ top: 10, bottom: 10 })
Flex({ justifyContent: FlexAlign.SpaceAround }) {
Button('cancel')
.onClick(() => {
this.controller.close()
this.cancel()
}).backgroundColor(0xffffff).fontColor(Color.Black)
Button('confirm')
.onClick(() => {
this.controller.close()
this.confirm()
}).backgroundColor(0xffffff).fontColor(Color.Red)
}.margin({ bottom: 10 })
}
}
}
```
2. 页面内需要在构造器内进行接收,同时创建相应的函数操作。
```ts
dialogController: CustomDialogController = new CustomDialogController({
builder: CustomDialogExample({
cancel: this.onCancel,
confirm: this.onAccept,
}),
alignment: DialogAlignment.Default, // 可设置dialog的对齐方式,设定显示在底部或中间等,默认为底部显示
})
onCancel() {
console.info('Callback when the first button is clicked')
}
onAccept() {
console.info('Callback when the second button is clicked')
}
```
![zh-cn_image_0000001511421320](figures/zh-cn_image_0000001511421320.png)
# 进度条
Progress是进度条显示组件,显示内容通常为某次目标操作的当前进度。具体用法请参考[Progress](../reference/arkui-ts/ts-basic-components-progress.md)
## 创建进度条
Progress通过调用接口来创建,接口调用形式如下:
```ts
Progress(options: {value: number, total?: number, type?: ProgressType})
```
该接口用于创建type样式的进度条,其中value用于设置初始进度值,total用于设置进度总长度,type决定Progress样式。
```ts
Progress({ value: 24, total: 100, type: ProgressType.Linear }) // 创建一个进度总长为100,初始进度值为24的线性进度条
```
![create](figures/create.png)
## 设置进度条样式
Progress有5种可选类型,在创建时通过设置ProgressType枚举类型给type可选项指定Progress类型。其分别为:ProgressType.Linear(线性样式)、 ProgressType.Ring(环形无刻度样式)、ProgressType.ScaleRing(环形有刻度样式)、ProgressType.Eclipse(圆形样式)和ProgressType.Capsule(胶囊样式)。
- 线性样式进度条(默认类型)
>**说明:**
>
> 从API version9开始,组件高度大于宽度的时候自适应垂直显示,相等时仍然保持水平显示。
```ts
Progress({ value: 20, total: 100, type: ProgressType.Linear }).width(200).height(50)
Progress({ value: 20, total: 100, type: ProgressType.Linear }).width(50).height(200)
```
![zh-cn_image_0000001562700417](figures/zh-cn_image_0000001562700417.png)
- 环形无刻度样式进度条
```ts
// 从左往右,1号环形进度条,默认前景色为蓝色,默认strokeWidth进度条宽度为2.0vp
Progress({ value: 40, total: 150, type: ProgressType.Ring }).width(100).height(100)
// 从左往右,2号环形进度条
Progress({ value: 40, total: 150, type: ProgressType.Ring }).width(100).height(100)
.color(Color.Grey) // 进度条前景色为灰色
.style({ strokeWidth: 15}) // 设置strokeWidth进度条宽度为15.0vp
```
![progress_ring](figures/progress_ring.png)
- 环形有刻度样式进度条
```ts
Progress({ value: 20, total: 150, type: ProgressType.ScaleRing }).width(100).height(100)
.backgroundColor(Color.Black)
.style({ scaleCount: 20, scaleWidth: 5 }) // 设置环形有刻度进度条总刻度数为20,刻度宽度为5vp
Progress({ value: 20, total: 150, type: ProgressType.ScaleRing }).width(100).height(100)
.backgroundColor(Color.Black)
.style({ strokeWidth: 15, scaleCount: 20, scaleWidth: 5 }) // 设置环形有刻度进度条宽度15,总刻度数为20,刻度宽度为5vp
Progress({ value: 20, total: 150, type: ProgressType.ScaleRing }).width(100).height(100)
.backgroundColor(Color.Black)
.style({ strokeWidth: 15, scaleCount: 20, scaleWidth: 3 }) // 设置环形有刻度进度条宽度15,总刻度数为20,刻度宽度为3vp
```
![progress_scalering](figures/progress_scalering.png)
- 圆形样式进度条
```ts
// 从左往右,1号圆形进度条,默认前景色为蓝色
Progress({ value: 10, total: 150, type: ProgressType.Eclipse }).width(100).height(100)
// 从左往右,2号圆形进度条,指定前景色为灰色
Progress({ value: 20, total: 150, type: ProgressType.Eclipse }).color(Color.Grey).width(100).height(100)
```
![progress_circle](figures/progress_circle.png)
- 胶囊样式进度条
>**说明:**
>
>- 头尾两端圆弧处的进度展示效果与ProgressType.Eclipse样式相同;
>- 中段处的进度展示效果为矩形状长条,与ProgressType.Linear线性样式相似;
>
>- 组件高度大于宽度的时候自适应垂直显示。
```ts
Progress({ value: 10, total: 150, type: ProgressType.Capsule }).width(100).height(50)
Progress({ value: 20, total: 150, type: ProgressType.Capsule }).width(50).height(100).color(Color.Grey)
Progress({ value: 50, total: 150, type: ProgressType.Capsule }).width(50).height(100).backgroundColor(Color.Black)
```
![progress_captule](figures/progress_captule.png)
## 场景示例
更新当前进度值,如应用安装进度条。可通过点击Button增加progressValue,.value()属性将progressValue设置给Progress组件,进度条组件即会触发刷新,更新当前进度。
```ts
@Entry
@Component
struct ProgressCase1 {
@State progressValue: number = 0 // 设置进度条初始值为0
build() {
Column() {
Column() {
Progress({value:0, total:100, type:ProgressType.Capsule}).width(200).height(50)
.style({strokeWidth:50}).value(this.progressValue)
Row().width('100%').height(5)
Button("进度条+5")
.onClick(()=>{
this.progressValue += 5
if (this.progressValue > 100){
this.progressValue = 0
}
})
}
}.width('100%').height('100%')
}
}
```
![progress](figures/progress.gif)
# 单选框
Radio是单选框组件,通常用于提供相应的用户交互选择项,同一组的Radio中只有一个可以被选中。具体用法请参考[Radio](../reference/arkui-ts/ts-basic-components-radio.md)
## 创建单选框
Radio通过调用接口来创建,接口调用形式如下:
```ts
Radio(options: {value: string, group: string})
```
该接口用于创建一个单选框,其中value是单选框的名称,group是单选框的所属群组名称。checked属性可以设置单选框的状态,状态分别为false和true时,设置为true时表示单选框被选中。Radio仅支持选中和未选中两种样式,不支持自定义颜色和形状。
```ts
Radio({ value: 'Radio1', group: 'radioGroup' })
.checked(false)
Radio({ value: 'Radio2', group: 'radioGroup' })
.checked(true)
```
![zh-cn_image_0000001562820821](figures/zh-cn_image_0000001562820821.png)
## 添加事件
除支持[通用事件](../reference/arkui-ts/ts-universal-events-click.md)外,Radio通常用于选中后触发某些操作,可以绑定onChange事件来响应选中操作后的自定义行为。
```ts
Radio({ value: 'Radio1', group: 'radioGroup' })
.onChange((isChecked: boolean) => {
if(isChecked) {
//需要执行的操作
}
})
Radio({ value: 'Radio2', group: 'radioGroup' })
.onChange((isChecked: boolean) => {
if(isChecked) {
//需要执行的操作
}
})
```
## 场景示例
通过点击Radio切换声音模式。
```ts
// xxx.ets
import promptAction from '@ohos.promptAction';
@Entry
@Component
struct RadioExample {
build() {
Row() {
Column() {
Radio({ value: 'Radio1', group: 'radioGroup' }).checked(true)
.height(50)
.width(50)
.onChange((isChecked: boolean) => {
if(isChecked) {
// 切换为响铃模式
promptAction.showToast({ message: 'Ringing mode.' })
}
})
Text('Ringing')
}
Column() {
Radio({ value: 'Radio2', group: 'radioGroup' })
.height(50)
.width(50)
.onChange((isChecked: boolean) => {
if(isChecked) {
// 切换为振动模式
promptAction.showToast({ message: 'Vibration mode.' })
}
})
Text('Vibration')
}
Column() {
Radio({ value: 'Radio3', group: 'radioGroup' })
.height(50)
.width(50)
.onChange((isChecked: boolean) => {
if(isChecked) {
// 切换为静音模式
promptAction.showToast({ message: 'Silent mode.' })
}
})
Text('Silent')
}
}.height('100%').width('100%').justifyContent(FlexAlign.Center)
}
}
```
![zh-cn_image_0000001562700457](figures/zh-cn_image_0000001562700457.png)
# 切换按钮
Toggle组件提供状态按钮样式,勾选框样式及开关样式,一般用于两种状态之间的切换。具体用法请参考[Toggle](../reference/arkui-ts/ts-basic-components-toggle.md)
## 创建切换按钮
Toggle通过调用接口来创建,接口调用形式如下:
```ts
Toggle(options: { type: ToggleType, isOn?: boolean })
```
该接口用于创建切换按钮,其中ToggleType为开关类型,包括Button、Checkbox和Switch,isOn为切换按钮的状态,接口调用有以下两种形式:
- 创建不包含子组件的Toogle。
当ToggleType为Checkbox或者Switch时,用于创建不包含子组件的Toggle:
```ts
Toggle({ type: ToggleType.Checkbox, isOn: false })
Toggle({ type: ToggleType.Checkbox, isOn: true })
```
![zh-cn_image_0000001562940485](figures/zh-cn_image_0000001562940485.png)
```ts
Toggle({ type: ToggleType.Switch, isOn: false })
Toggle({ type: ToggleType.Switch, isOn: true })
```
![zh-cn_image_0000001511421228](figures/zh-cn_image_0000001511421228.png)
- 创建包含子组件的Toggle。
当ToggleType为Button时,如果子组件有文本设置,则相应的文本内容会显示在按钮内部。
```ts
Toggle({ type: ToggleType.Button, isOn: false }) {
Text('status button')
.fontColor('#182431')
.fontSize(12)
}.width(100)
Toggle({ type: ToggleType.Button, isOn: true }) {
Text('status button')
.fontColor('#182431')
.fontSize(12)
}.width(100)
```
![zh-cn_image_0000001511900404](figures/zh-cn_image_0000001511900404.png)
## 自定义样式
- 通过selectedColor属性设置Toggle打开选中后的背景颜色。
```ts
Toggle({ type: ToggleType.Button, isOn: true }) {
Text('status button')
.fontColor('#182431')
.fontSize(12)
}.width(100).selectedColor(Color.Pink)
Toggle({ type: ToggleType.Checkbox, isOn: true })
.selectedColor(Color.Pink)
Toggle({ type: ToggleType.Switch, isOn: true })
.selectedColor(Color.Pink)
```
![zh-cn_image_0000001563060657](figures/zh-cn_image_0000001563060657.png)
- 通过switchPointColor属性设置Switch类型的圆形滑块颜色,仅对type为ToggleType.Switch生效。
```ts
Toggle({ type: ToggleType.Switch, isOn: false })
.switchPointColor(Color.Pink)
Toggle({ type: ToggleType.Switch, isOn: true })
.switchPointColor(Color.Pink)
```
![zh-cn_image_0000001511421232](figures/zh-cn_image_0000001511421232.png)
## 添加事件
除支持通用事件外,Toggle通常用于选中和取消选中后触发某些操作,可以绑定onChange事件来响应操作后的自定义行为。
```ts
Toggle({ type: ToggleType.Switch, isOn: false })
.onChange((isOn: boolean) => {
if(isOn) {
// 需要执行的操作
}
})
```
## 场景示例
Toggle可用于切换蓝牙开关状态。
```ts
// xxx.ets
import prompt from '@ohos.promptAction';
@Entry
@Component
struct ToggleExample {
build() {
Column() {
Row() {
Text("Bluetooth Mode")
.height(50)
.fontSize(16)
}
Row() {
Text("Bluetooth")
.height(50)
.padding({left: 10})
.fontSize(16)
.textAlign(TextAlign.Start)
.backgroundColor(0xFFFFFF)
Toggle({ type: ToggleType.Switch })
.margin({left: 200, right: 10})
.onChange((isOn: boolean) => {
if(isOn) {
promptAction.showToast({ message: 'Bluetooth is on.' })
} else {
promptAction.showToast({ message: 'Bluetooth is off.' })
}
})
}
.backgroundColor(0xFFFFFF)
}
.padding(10)
.backgroundColor(0xDCDCDC)
.width('100%')
.height('100%')
}
}
```
![zh-cn_image_0000001511740448](figures/zh-cn_image_0000001511740448.png)
# 文本显示
Text是文本组件,通常用于展示用户的视图,如显示文章的文字。具体用法可参考[Text](../reference/arkui-ts/ts-basic-components-text.md)
## 创建文本
Text可通过以下两种方式来创建:
- string字符串
```ts
Text('我是一段文本')
```
![zh-cn_image_0000001563060685](figures/zh-cn_image_0000001563060685.png)
- 引用Resource资源
资源引用类型可以通过$r创建Resource类型对象,文件位置为/resources/base/element/string.json。
```ts
Text($r('app.string.module_desc'))
.baselineOffset(0)
.fontSize(30)
.border({ width: 1 })
.padding(10)
.width(300)
```
![zh-cn_image_0000001511580872](figures/zh-cn_image_0000001511580872.png)
## 添加子组件
[Span](../reference/arkui-ts/ts-basic-components-span.md)只能作为Text组件的子组件显示文本内容。可以在一个Text内添加多个Span来显示一段信息,例如产品说明书、承诺书等。
- 创建Span。
Span组件需要写到Text组件内,单独写Span组件不会显示信息,Text与Span同时配置文本内容内容时,Span内容覆盖Text内容。
```ts
Text('我是Text') {
Span('我是Span')
}
.padding(10)
.borderWidth(1)
```
![zh-cn_image_0000001562700441](figures/zh-cn_image_0000001562700441.png)
- 设置文本装饰线及颜色。
通过decoration设置文本装饰线及颜色。
```ts
Text() {
Span('我是Span1,').fontSize(16).fontColor(Color.Grey)
.decoration({ type: TextDecorationType.LineThrough, color: Color.Red })
Span('我是Span2').fontColor(Color.Blue).fontSize(16)
.fontStyle(FontStyle.Italic)
.decoration({ type: TextDecorationType.Underline, color: Color.Black })
Span(',我是Span3').fontSize(16).fontColor(Color.Grey)
.decoration({ type: TextDecorationType.Overline, color: Color.Green })
}
.borderWidth(1)
.padding(10)
```
![zh-cn_image_0000001562700437](figures/zh-cn_image_0000001562700437.png)
- 通过textCase设置文字一直保持大写或者小写状态。
```ts
Text() {
Span('I am Upper-span').fontSize(12)
.textCase(TextCase.UpperCase)
}
.borderWidth(1)
.padding(10)
```
![zh-cn_image_0000001562940525](figures/zh-cn_image_0000001562940525.png)
- 添加事件。
由于Span组件无尺寸信息,事件仅支持点击事件onClick。
```ts
Text() {
Span('I am Upper-span').fontSize(12)
.textCase(TextCase.UpperCase)
.onClick(()=>{
console.info('我是Span——onClick')
})
}
```
## 自定义文本样式
- 通过textAlign属性设置文本对齐样式。
```ts
Text('左对齐')
.width(300)
.textAlign(TextAlign.Start)
.border({ width: 1 })
.padding(10)
Text('中间对齐')
.width(300)
.textAlign(TextAlign.Center)
.border({ width: 1 })
.padding(10)
Text('右对齐')
.width(300)
.textAlign(TextAlign.End)
.border({ width: 1 })
.padding(10)
```
![zh-cn_image_0000001511421260](figures/zh-cn_image_0000001511421260.png)
- 通过textOverflow属性控制文本超长处理,textOverflow需配合maxLines一起使用(默认情况下文本自动折行)。
```ts
Text('This is the setting of textOverflow to Clip text content This is the setting of textOverflow to None text content. This is the setting of textOverflow to Clip text content This is the setting of textOverflow to None text content.')
.width(250)
.textOverflow({ overflow: TextOverflow.None })
.maxLines(1)
.fontSize(12)
.border({ width: 1 }).padding(10)
Text('我是超长文本,超出的部分显示省略号。I am an extra long text, with ellipses displayed for any excess。')
.width(250)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.maxLines(1)
.fontSize(12)
.border({ width: 1 }).padding(10)
```
![zh-cn_image_0000001563060693](figures/zh-cn_image_0000001563060693.png)
![zh-cn_image_0000001563060701](figures/zh-cn_image_0000001563060701.png)
- 通过lineHeight属性设置文本行高。
```ts
Text('This is the text with the line height set. This is the text with the line height set.')
.width(300).fontSize(12).border({ width: 1 }).padding(10)
Text('This is the text with the line height set. This is the text with the line height set.')
.width(300).fontSize(12).border({ width: 1 }).padding(10)
.lineHeight(20)
```
![zh-cn_image_0000001511740480](figures/zh-cn_image_0000001511740480.png)
- 通过decoration属性设置文本装饰线样式及其颜色。
```ts
Text('This is the text')
.decoration({
type: TextDecorationType.LineThrough,
color: Color.Red
})
.borderWidth(1).padding(10).margin(5)
Text('This is the text')
.decoration({
type: TextDecorationType.Overline,
color: Color.Red
})
.borderWidth(1).padding(10).margin(5)
Text('This is the text')
.decoration({
type: TextDecorationType.Underline,
color: Color.Red
})
.borderWidth(1).padding(10).margin(5)
```
![zh-cn_image_0000001511580888](figures/zh-cn_image_0000001511580888.png)
- 通过baselineOffset属性设置文本基线的偏移量。
```ts
Text('This is the text content with baselineOffset 0.')
.baselineOffset(0)
.fontSize(12)
.border({ width: 1 })
.padding(10)
.width('100%')
.margin(5)
Text('This is the text content with baselineOffset 30.')
.baselineOffset(30)
.fontSize(12)
.border({ width: 1 })
.padding(10)
.width('100%')
.margin(5)
Text('This is the text content with baselineOffset -20.')
.baselineOffset(-20)
.fontSize(12)
.border({ width: 1 })
.padding(10)
.width('100%')
.margin(5)
```
![zh-cn_image_0000001562820789](figures/zh-cn_image_0000001562820789.png)
- 通过letterSpacing属性设置文本字符间距。
```ts
Text('This is the text content with letterSpacing 0.')
.letterSpacing(0)
.fontSize(12)
.border({ width: 1 })
.padding(10)
.width('100%')
.margin(5)
Text('This is the text content with letterSpacing 3.')
.letterSpacing(3)
.fontSize(12)
.border({ width: 1 })
.padding(10)
.width('100%')
.margin(5)
Text('This is the text content with letterSpacing -1.')
.letterSpacing(-1)
.fontSize(12)
.border({ width: 1 })
.padding(10)
.width('100%')
.margin(5)
```
![zh-cn_image_0000001562940513](figures/zh-cn_image_0000001562940513.png)
- 通过minFontSize与maxFontSize自适应字体大小,minFontSize设置文本最小显示字号,maxFontSize设置文本最大显示字号,minFontSize与maxFontSize必须搭配同时使用,以及需配合maxline或布局大小限制一起使用,单独设置不生效。
```ts
Text('我的最大字号为30,最小字号为5,宽度为250,maxLines为1')
.width(250)
.maxLines(1)
.maxFontSize(30)
.minFontSize(5)
.border({ width: 1 })
.padding(10)
.margin(5)
Text('我的最大字号为30,最小字号为5,宽度为250,maxLines为2')
.width(250)
.maxLines(2)
.maxFontSize(30)
.minFontSize(5)
.border({ width: 1 })
.padding(10)
.margin(5)
Text('我的最大字号为30,最小字号为15,宽度为250,高度为50')
.width(250)
.height(50)
.maxFontSize(30)
.minFontSize(15)
.border({ width: 1 })
.padding(10)
.margin(5)
Text('我的最大字号为30,最小字号为15,宽度为250,高度为100')
.width(250)
.height(100)
.maxFontSize(30)
.minFontSize(15)
.border({ width: 1 })
.padding(10)
.margin(5)
```
![zh-cn_image_0000001511740472](figures/zh-cn_image_0000001511740472.png)
- 通过textCase属性设置文本大小写。
```ts
Text('This is the text content with textCase set to Normal.')
.textCase(TextCase.Normal)
.padding(10)
.border({ width: 1 })
.padding(10)
.margin(5)
// 文本全小写展示
Text('This is the text content with textCase set to LowerCase.')
.textCase(TextCase.LowerCase)
.border({ width: 1 })
.padding(10)
.margin(5)
// 文本全大写展示
Text('This is the text content with textCase set to UpperCase.')
.textCase(TextCase.UpperCase)
.border({ width: 1 })
.padding(10)
.margin(5)
```
![zh-cn_image_0000001562940529](figures/zh-cn_image_0000001562940529.png)
- 通过copyOption属性设置文本是否可复制粘贴。
```ts
Text("这是一段可复制文本")
.fontSize(30)
.copyOption(CopyOptions.InApp)
```
![zh-cn_image_0000001511580868](figures/zh-cn_image_0000001511580868.png)
## 添加事件
Text组件可以添加通用事件,可以绑定onClick、onTouch等事件来响应操作。
```ts
Text('点我')
.onClick(()=>{
console.info('我是Text的点击响应事件');
})
```
## 场景示例
```ts
// xxx.ets
@Entry
@Component
struct TextExample {
build() {
Column() {
Row() {
Text("1").fontSize(14).fontColor(Color.Red).margin({ left: 10, right: 10 })
Text("我是热搜词条1")
.fontSize(12)
.fontColor(Color.Blue)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.fontWeight(300)
Text("")
.margin({ left: 6 })
.textAlign(TextAlign.Center)
.fontSize(10)
.fontColor(Color.White)
.fontWeight(600)
.backgroundColor(0x770100)
.borderRadius(5)
.width(15)
.height(14)
}.width('100%').margin(5)
Row() {
Text("2").fontSize(14).fontColor(Color.Red).margin({ left: 10, right: 10 })
Text("我是热搜词条2 我是热搜词条2 我是热搜词条2 我是热搜词条2 我是热搜词条2")
.fontSize(12)
.fontColor(Color.Blue)
.fontWeight(300)
.constraintSize({ maxWidth: 200 })
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text("")
.margin({ left: 6 })
.textAlign(TextAlign.Center)
.fontSize(10)
.fontColor(Color.White)
.fontWeight(600)
.backgroundColor(0xCC5500)
.borderRadius(5)
.width(15)
.height(14)
}.width('100%').margin(5)
Row() {
Text("3").fontSize(14).fontColor(Color.Orange).margin({ left: 10, right: 10 })
Text("我是热搜词条3")
.fontSize(12)
.fontColor(Color.Blue)
.fontWeight(300)
.maxLines(1)
.constraintSize({ maxWidth: 200 })
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text("")
.margin({ left: 6 })
.textAlign(TextAlign.Center)
.fontSize(10)
.fontColor(Color.White)
.fontWeight(600)
.backgroundColor(0xCC5500)
.borderRadius(5)
.width(15)
.height(14)
}.width('100%').margin(5)
Row() {
Text("4").fontSize(14).fontColor(Color.Grey).margin({ left: 10, right: 10 })
Text("我是热搜词条4 我是热搜词条4 我是热搜词条4 我是热搜词条4 我是热搜词条4")
.fontSize(12)
.fontColor(Color.Blue)
.fontWeight(300)
.constraintSize({ maxWidth: 200 })
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}.width('100%').margin(5)
}.width('100%')
}
}
```
![zh-cn_image_0000001562820805](figures/zh-cn_image_0000001562820805.png)
# 文本输入
TextInput、TextArea是输入框组件,通常用于响应用户的输入操作,比如评论区的输入、聊天框的输入、表格的输入等,也可以结合其它组件构建功能页面,例如登录注册页面。具体用法参考[TextInput](../reference/arkui-ts/ts-basic-components-textinput.md)[TextArea](../reference/arkui-ts/ts-basic-components-textarea.md)
## 创建输入框
TextInput为单行输入框、TextArea为多行输入框。通过以下接口来创建。
```ts
TextArea(value?:{placeholder?: ResourceStr, text?: ResourceStr, controller?: TextAreaController})
```
```ts
TextInput(value?:{placeholder?: ResourceStr, text?: ResourceStr, controller?: TextInputController})
```
- 单行输入框
```ts
TextInput()
```
![zh-cn_image_0000001511580844](figures/zh-cn_image_0000001511580844.png)
- 多行输入框
```ts
TextArea()
```
![zh-cn_image_0000001562940481](figures/zh-cn_image_0000001562940481.png)
多行输入框文字超出一行时会自动折行。
```ts
TextArea({text:"我是TextArea我是TextArea我是TextArea我是TextArea"}).width(300)
```
![zh-cn_image_0000001511580836](figures/zh-cn_image_0000001511580836.png)
## 设置输入框类型
TextInput有5种可选类型,分别为Normal基本输入模式、Password密码输入模式、Email邮箱地址输入模式、Number纯数字输入模式、PhoneNumber电话号码输入模式。通过type属性进行设置:
- 基本输入模式(默认类型)
```ts
TextInput()
.type(InputType.Normal)
```
![zh-cn_image_0000001562820765](figures/zh-cn_image_0000001562820765.png)
- 密码输入模式
```ts
TextInput()
.type(InputType.Password)
```
![zh-cn_image_0000001511580840](figures/zh-cn_image_0000001511580840.png)
## 自定义样式
- 设置无输入时的提示文本。
TextInput({placeholder:'我是提示文本'})
```ts
TextInput({placeholder:'我是提示文本'})
```
![zh-cn_image_0000001511900400](figures/zh-cn_image_0000001511900400.png)
- 设置输入框当前的文本内容。
```ts
TextInput({placeholder:'我是提示文本',text:'我是当前文本内容'})
```
![zh-cn_image_0000001562820761](figures/zh-cn_image_0000001562820761.png)
- 添加backgroundColor改变输入框的背景颜色。
```ts
TextInput({placeholder:'我是提示文本',text:'我是当前文本内容'})
.backgroundColor(Color.Pink)
```
![zh-cn_image_0000001511740444](figures/zh-cn_image_0000001511740444.png)
更丰富的样式可以结合[通用属性](../reference/arkui-ts/ts-universal-attributes-size.md)实现。
## 添加事件
文本框主要用于获取用户输入的信息,把信息处理成数据进行上传,绑定onChange事件可以获取输入框内改变的内容。用户也可以使用通用事件来进行相应的交互操作。
```ts
TextInput()
.onChange((value: string) => {
console.info(value);
})
.onFocus(() => {
console.info('获取焦点');
})
```
## 场景示例
用于表单的提交,在用户登录/注册页面,用户的登录或注册的输入操作。
```ts
@Entry
@Component
struct TextInputSample {
build() {
Column() {
TextInput({ placeholder: 'input your username' }).margin({ top: 20 })
.onSubmit((EnterKeyType)=>{
console.info(EnterKeyType+'输入法回车键的类型值')
})
TextInput({ placeholder: 'input your password' }).type(InputType.Password).margin({ top: 20 })
.onSubmit((EnterKeyType)=>{
console.info(EnterKeyType+'输入法回车键的类型值')
})
Button('Sign in').width(150).margin({ top: 20 })
}.padding(20)
}
}
```
![zh-cn_image_0000001563060653](figures/zh-cn_image_0000001563060653.png)
# 视频播放
Video组件用于播放视频文件并控制其播放状态,常用于为短视频应用和应用内部视频的列表页面。当视频完整出现时会自动播放,用户点击视频区域则会暂停播放,同时显示播放进度条,通过拖动播放进度条指定视频播放到具体位置。具体用法请参考[Video](../reference/arkui-ts/ts-media-components-video.md)
## 创建视频组件
Video通过调用接口来创建,接口调用形式如下:
```ts
Video(value: {src?: string | Resource, currentProgressRate?: number | string | PlaybackSpeed, previewUri?: string | PixelMap | Resource, controller?: VideoController})
```
该接口用于创建视频播放组件。其中,src指定视频播放源的路径,加载方式请参考[加载视频资源](#加载视频资源),currentProgressRate用于设置视频播放倍速,previewUri指定视频未播放时的预览图片路径,controller设置视频控制器,用于自定义控制视频。
## 加载视频资源
Video组件支持加载本地视频和网络视频。
### 加载本地视频
- 普通本地视频。
加载本地视频时,首先在本地rawfile目录指定对应的文件,如下图所示。
![zh-cn_image_0000001562700409](figures/zh-cn_image_0000001562700409.png)
再使用资源访问符$rawfile()引用视频资源。
```ts
@Component
export struct VideoPlayer{
private controller:VideoController;
private previewUris: Resource = $r ('app.media.preview');
private innerResource: Resource = $rawfile('videoTest.mp4');
build(){
Column() {
Video({
src: this.innerResource,
previewUri: this.previewUris,
controller: this.controller
})
}
}
}
```
- [Data Ability](../application-models/dataability-overview.md)提供的视频路径带有dataability://前缀,使用时确保对应视频资源存在即可。
```ts
@Component
export struct VideoPlayer{
private controller:VideoController;
private previewUris: Resource = $r ('app.media.preview');
private videosrc: string= 'dataability://device_id/com.domainname.dataability.videodata/video/10'
build(){
Column() {
Video({
src: this.videosrc,
previewUri: this.previewUris,
controller: this.controller
})
}
}
}
```
### 加载网络视频
加载网络视频时,需要申请权限ohos.permission.INTERNET,具体申请方式请参考[权限申请声明](../security/accesstoken-guidelines.md)。此时,Video的src属性为网络视频的链接。
```ts
@Component
export struct VideoPlayer{
private controller:VideoController;
private previewUris: Resource = $r ('app.media.preview');
private videosrc: string= 'https://www.example.com/example.mp4' // 使用时请替换为实际视频加载网址
build(){
Column() {
Video({
src: this.videosrc,
previewUri: this.previewUris,
controller: this.controller
})
}
}
}
```
## 添加属性
Video组件[属性](../reference/arkui-ts/ts-media-components-video.md#属性)主要用于设置视频的播放形式。例如设置视频播放是否静音、播放时是否显示控制条等。
```ts
@Component
export struct VideoPlayer {
private controller: VideoController;
build() {
Column() {
Video({
controller: this.controller
})
.muted(false) //设置是否静音
.controls(false) //设置是否显示默认控制条
.autoPlay(false) //设置是否自动播放
.loop(false) //设置是否循环播放
.objectFit(ImageFit.Contain) //设置视频适配模式
}
}
}
```
## 事件调用
Video组件回调事件主要为播放开始、暂停结束、播放失败、视频准备和操作进度条等事件,除此之外,Video组件也支持通用事件的调用,如点击、触摸等事件的调用。详细的事件请参考[事件说明](../reference/arkui-ts/ts-media-components-video.md#事件)
```ts
@Entry
@Component
struct VideoPlayer{
private controller:VideoController;
private previewUris: Resource = $r ('app.media.preview');
private innerResource: Resource = $rawfile('videoTest.mp4');
build(){
Column() {
Video({
src: this.innerResource,
previewUri: this.previewUris,
controller: this.controller
})
.onUpdate((event) => { //更新事件回调
console.info("Video update.");
})
.onPrepared((event) => { //准备事件回调
console.info("Video prepared.");
})
.onError(() => { //失败事件回调
console.info("Video error.");
})
}
}
}
```
## Video控制器使用
Video控制器主要用于控制视频的状态,包括播放、暂停、停止以及设置进度等,详细的使用请参考[VideoController使用说明](../reference/arkui-ts/ts-media-components-video.md#videocontroller)
- 默认控制器
默认的控制器支持视频的开始、暂停、进度调整、全屏显示四项基本功能。
```ts
@Entry
@Component
struct VideoGuide {
@State videoSrc: Resource = $rawfile('videoTest.mp4')
@State previewUri: string = 'common/videoIcon.png'
@State curRate: PlaybackSpeed = PlaybackSpeed.Speed_Forward_1_00_X
build() {
Row() {
Column() {
Video({
src: this.videoSrc,
previewUri: this.previewUri,
currentProgressRate: this.curRate
})
}
.width('100%')
}
.height('100%')
}
}
```
- 自定义控制器
使用自定义的控制器,先将默认控制器关闭掉,之后可以使用button以及slider等组件进行自定义的控制与显示,适合自定义较强的场景下使用。
```ts
@Entry
@Component
struct VideoGuide {
@State videoSrc: Resource = $rawfile('videoTest.mp4')
@State previewUri: string = 'common/videoIcon.png'
@State curRate: PlaybackSpeed = PlaybackSpeed.Speed_Forward_1_00_X
@State isAutoPlay: boolean = false
@State showControls: boolean = true
@State sliderStartTime: string = '';
@State currentTime: number = 0;
@State durationTime: number = 0;
@State durationStringTime: string ='';
controller: VideoController = new VideoController()
build() {
Row() {
Column() {
Video({
src: this.videoSrc,
previewUri: this.previewUri,
currentProgressRate: this.curRate,
controller: this.controller
}).controls(false).autoPlay(true)
.onPrepared((event)=>{
this.durationTime = event.duration
})
.onUpdate((event)=>{
this.currentTime =event.time
})
Row() {
Text(JSON.stringify(this.currentTime) + 's')
Slider({
value: this.currentTime,
min: 0,
max: this.durationTime
})
.onChange((value: number, mode: SliderChangeMode) => {
this.controller.setCurrentTime(value);
}).width("90%")
Text(JSON.stringify(this.durationTime) + 's')
}
.opacity(0.8)
.width("100%")
}
.width('100%')
}
.height('40%')
}
}
```
## 其他说明
Video组件已经封装好了视频播放的基础能力,开发者无需进行视频实例的创建,视频信息的设置获取,只需要设置数据源以及基础信息即可播放视频,相对扩展能力较弱。如果开发者想自定义视频播放,还请参考[媒体系统播放音视频](../media/video-playback.md)
# XComponent
[XComponent](../reference/arkui-ts/ts-basic-components-xcomponent.md)组件作为一种绘制组件,通常用于满足开发者较为复杂的自定义绘制需求,例如相机预览流的显示和游戏画面的绘制。
其可通过指定其type字段来实现不同的功能,主要有两个“surface”和“component”字段可供选择。
对于“surface”类型,开发者可将相关数据传入XComponent单独拥有的“surface”来渲染画面。
对于“component”类型则主要用于实现动态加载显示内容的目的。
## surface类型
XComponent设置为surface类型时通常用于EGL/OpenGLES和媒体数据写入,并将其显示在XComponent组件上。
设置为“surface“类型时XComponent组件可以和其他组件一起进行布局和渲染。
同时XComponent又拥有单独的“surface“,可以为开发者在native侧提供native window用来创建EGL/OpenGLES环境,进而使用标准的OpenGL ES开发。
除此之外,媒体相关应用(视频、相机等)也可以将相关数据写入XComponent所提供的surface,从而实现呈现相应画面。
## 使用EGL/OpenGLES渲染
### native侧代码开发要点
OpenHarmony的应用如果要通过js来桥接native,一般需要使用napi接口来处理js交互,XComponent同样不例外,具体使用请参考[Native API在应用工程中的使用指导](../napi/napi-guidelines.md)
Native侧处理js逻辑的文件类型为so:
- 每个模块对应一个so
- so的命名规则为 lib{模块名}.so
对于使用XComponent进行标准OpenGL ES开发的场景,CMAKELists.txt文件内容大致如下:
```
cmake_minimum_required(VERSION 3.4.1)
project(XComponent) # 项目名称
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
# 头文件查找路径
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include
)
# 编译目标so,SHARED表示动态库
add_library(nativerender SHARED
xxx.cpp
)
# 查找相关库 (包括OpenGL ES相关库和XComponent提供的ndk接口)
find_library( EGL-lib
EGL )
find_library( GLES-lib
GLESv3 )
find_library( libace-lib
ace_ndk.z )
# 编译so所需要的依赖
target_link_libraries(nativerender PUBLIC ${EGL-lib} ${GLES-lib} ${libace-lib} libace_napi.z.so libc++.a)
```
### Napi模块注册
```c++
static napi_value Init(napi_env env, napi_value exports)
{
// 定义暴露在模块上的方法
napi_property_descriptor desc[] ={
DECLARE_NAPI_FUNCTION("changeColor", PluginRender::NapiChangeColor),
};
// 通过此接口开发者可在exports上挂载native方法(即上面的PluginRender::NapiChangeColor),exports会通过js引擎绑定到js层的一个js对象
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
return exports;
}
static napi_module nativerenderModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init, // 指定加载对应模块时的回调函数
.nm_modname = "nativerender", // 指定模块名称,对于XComponent相关开发,这个名称必须和ArkTS侧XComponent中libraryname的值保持一致
.nm_priv = ((void*)0),
.reserved = { 0 },
};
extern "C" __attribute__((constructor)) void RegisterModule(void)
{
// 注册so模块
napi_module_register(&nativerenderModule);c
}
```
### 解析XComponent组件的NativeXComponent实例
NativeXComponent为XComponent提供了在native层的实例,可作为js层和native层XComponent绑定的桥梁。XComponent所提供的的NDK接口都依赖于该实例。具体NKD接口可参考[Native XComponent](../reference/native-apis/_o_h___native_x_component.md)
可以在模块被加载时的回调内(即[Napi模块注册](#napi模块注册)中的Init函数)解析获得NativeXComponent实例
```c++
{
// ...
napi_status status;
napi_value exportInstance = nullptr;
OH_NativeXComponent *nativeXComponent = nullptr;
// 用来解析出被wrap了NativeXComponent指针的属性
status = napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance);
if (status != napi_ok) {
return false;
}
// 通过napi_unwrap接口,解析出NativeXComponent的实例指针
status = napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent));
// ...
}
```
### 注册XComponent事件回调
依赖[解析XComponent组件的NativeXComponent实例](#解析xcomponent组件的nativexcomponent实例)拿到的NativeXComponent指针,通过OH_NativeXComponent_RegisterCallback接口进行回调注册
```c++
{
...
OH_NativeXComponent *nativeXComponent = nullptr;
// 解析出NativeXComponent实例
OH_NativeXComponent_Callback callback;
callback->OnSurfaceCreated = OnSurfaceCreatedCB; // surface创建成功后触发,开发者可以从中获取native window的句柄
callback->OnSurfaceChanged = OnSurfaceChangedCB; // surface发生变化后触发,开发者可以从中获取native window的句柄以及XComponent的变更信息
callback->OnSurfaceDestroyed = OnSurfaceDestroyedCB; // surface销毁时触发,开发者可以在此释放资源
callback->DispatchTouchEvent = DispatchTouchEventCB; // XComponent的touch事件回调接口,开发者可以从中获得此次touch事件的信息
OH_NativeXComponent_RegisterCallback(nativeXComponent, callback);
...
}
```
### 创建EGL/OpenGLES环境
在注册的OnSurfaceCreated回调中开发者能拿到native window的句柄(其本质就是XComponent所单独拥有的surface),因此可以在这里创建应用自己的EGL/OpenGLES开发环境,由此开始具体渲染逻辑的开发。
```c++
EGLCore* eglCore_; // EGLCore为封装了OpenGL相关接口的类
uint64_t width_;
uint64_t height_;
void OnSurfaceCreatedCB(OH_NativeXComponent* component, void* window)
{
int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window, &width_, &height_);
if (ret === OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
eglCore_->GLContextInit(window, width_, height_); // 初始化OpenGL环境
}
}
```
### ArkTS侧语法介绍
开发者在ArkTS侧使用如下代码即可用XComponent组件进行利用EGL/OpenGLES渲染的开发。
```ts
XComponent({ id: 'xcomponentId1', type: 'surface', libraryname: 'nativerender' })
.onLoad((context) => {})
.onDestroy(() => {})
```
- id : 与XComponent组件为一一对应关系,不可重复。通常开发者可以在native侧通过OH_NativeXComponent_GetXComponentId接口来获取对应的id从而绑定对应的XComponent。
- libraryname:加载模块的名称,必须与在native侧Napi模块注册时nm_modname的名字一致。
>**说明:**
>
> 应用加载模块实现跨语言调用有两种方式:
>
> 1. 使用NAPI的import方式加载:
>
> ```ts
> import nativerender from "libnativerender.so"
> ```
>
> 2. 使用XComponent组件加载,本质也是使用了NAPI机制来加载。
> 该加载方式和import加载方式的区别在于,在加载动态库是会将XComponent的NativeXComponent实例暴露到应用的native层中,从而让开发者可以使用XComponent的NDK接口。
- onLoad事件
- 触发时刻:XComponent准备好surface后触发。
- 参数context:其上面挂载了暴露在模块上的native方法,使用方法类似于利用 import context2 from "libnativerender.so" 直接加载模块后获得的context2实例。
- 时序:onLoad事件的触发和Surface相关,其和native侧的OnSurfaceCreated的时序如下图:
![图片2](figures/图片2.png)
- onDestroy事件
触发时刻:XComponent组件被销毁时触发与一般ArkUI的组件销毁时机一致,其和native侧的OnSurfaceDestroyed的时序如下图:
![图片3](figures/图片3.png)
### 媒体数据写入
XComponent所持有的Surface符合“生产者-消费者”模型
OpenHarmony上Camera、VideoPlayer等符合生产者设计的部件都可以将数据写入XComponent持有的surface并通过XComponent显示。
![图片1](figures/图片1.png)
开发者可通过绑定XComponentController获得对应XComponent的surfaceId(该id可以唯一确定一个surface),从而传给相应的部件接口。
```ts
@State surfaceId:string = "";
mXComponentController: XComponentController = new XComponentController();
XComponent({ id: '', type: 'surface', controller: this.mXComponentController })
.onLoad(() => {
this.surfaceId = this.mXComponentController.getXComponentSurfaceId()
})
```
具体部件接口可参考:[AVPlayer](../reference/apis/js-apis-media.md#avplayer9)[Camera](../reference/apis/js-apis-camera.md) 等。
### component类型
XComponent设置为component类型时通常用于在XComponent内部执行非UI逻辑以实现动态加载显示内容的目的。
>**说明:**
>
> type为"component"时,XComponent作为容器,子组件沿垂直方向布局:
>
> - 垂直方向上对齐格式:[FlexAlign](../reference/arkui-ts/ts-appendix-enums.md#flexalign).Start
>
> - 水平方向上对齐格式:[FlexAlign](../reference/arkui-ts/ts-appendix-enums.md#flexalign).Center
>
> 不支持所有的事件响应。
>
> 布局方式更改和事件响应均可通过挂载子组件来设置。
>
> 内部所写的非UI逻辑需要封装在一个或多个函数内。
### 场景示例
```ts
@Builder
function addText(label: string): void {
Text(label)
.fontSize(40)
}
@Entry
@Component
struct Index {
@State message: string = 'Hello XComponent'
@State messageCommon: string = 'Hello World'
build() {
Row() {
Column() {
XComponent({ id: 'xcomponentId-container', type: 'component' }) {
addText(this.message)
Divider()
.margin(4)
.strokeWidth(2)
.color('#F1F3F5')
.width("80%")
Column() {
Text(this.messageCommon)
.fontSize(30)
}
}
}
.width('100%')
}
.height('100%')
}
}
```
![zh-cn_image_0000001511900428](figures/zh-cn_image_0000001511900428.png)
# 键鼠事件
键鼠事件指键盘,鼠标外接设备的输入事件。
## 鼠标事件
支持的鼠标事件包含通过外设鼠标、触控板触发的事件。
鼠标事件可触发以下回调:
| 名称 | 描述 |
| ---------------------------------------- | ---------------------------------------- |
| onHover(event:&nbsp;(isHover:&nbsp;boolean)&nbsp;=&gt;&nbsp;void) | 鼠标进入或退出组件时触发该回调。<br/>isHover:表示鼠标是否悬浮在组件上,鼠标进入时为true,&nbsp;退出时为false。 |
| onMouse(event:&nbsp;(event?:&nbsp;MouseEvent)&nbsp;=&gt;&nbsp;void) | 当前组件被鼠标按键点击时或者鼠标在组件上悬浮移动时,触发该回调,event返回值包含触发事件时的时间戳、鼠标按键、动作、鼠标位置在整个屏幕上的坐标和相对于当前组件的坐标。 |
当组件绑定onHover回调时,可以通过[hoverEffect](../reference/arkui-ts/ts-universal-attributes-hover-effect.md)属性设置该组件的鼠标悬浮态显示效果。
**图1** 鼠标事件数据流  
![zh-cn_image_0000001511900504](figures/zh-cn_image_0000001511900504.png)
鼠标事件传递到ArkUI之后,会先判断鼠标事件是否是左键的按下/抬起/移动,然后做出不同响应:
- 是:鼠标事件先转换成相同位置的触摸事件,执行触摸事件的碰撞测试、手势判断和回调响应。接着去执行鼠标事件的碰撞测试和回调响应。
- 否:事件仅用于执行鼠标事件的碰撞测试和回调响应。
>**说明:**
>
>所有单指可响应的触摸事件/手势事件,均可通过鼠标左键来操作和响应。例如当我们需要开发单击Button跳转页面的功能、且需要支持手指点击和鼠标左键点击,那么只绑定一个点击事件(onClick)就可以实现该效果。若需要针对手指和鼠标左键的点击实现不一样的效果,可以在onClick回调中,使用回调参数中的source字段即可判断出当前触发事件的来源是手指还是鼠标。
### onHover
```ts
onHover(event: (isHover?: boolean) => void)
```
鼠标悬浮事件回调。参数isHover类型为boolean,表示鼠标进入组件或离开组件。该事件不支持自定义冒泡设置,默认父子冒泡。
若组件绑定了该接口,当鼠标指针从组件外部进入到该组件的瞬间会触发事件回调,参数isHover等于true;鼠标指针离开组件的瞬间也会触发该事件回调,参数isHover等于false。
>**说明:**
>
>事件冒泡:在一个树形结构中,当子节点处理完一个事件后,再将该事件交给它的父节点处理。
```ts
// xxx.ets
@Entry
@Component
struct MouseExample {
@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,又将组件变成了初始的样式。
![onHover](figures/onHover.gif)
### onMouse
```ts
onMouse(event: (event?: MouseEvent) => void)
```
鼠标事件回调。绑定该API的组件每当鼠标指针在该组件内产生行为(MouseAction)时,触发事件回调,参数为[MouseEvent](../reference/arkui-ts/ts-universal-mouse-key.md)对象,表示触发此次的鼠标事件。该事件支持自定义冒泡设置,默认父子冒泡。常见用于开发者自定义的鼠标行为逻辑处理。
开发者可以通过回调中的MouseEvent对象获取触发事件的坐标(screenX/screenY/x/y)、按键([MouseButton](../reference/arkui-ts/ts-appendix-enums.md#mousebutton))、行为([MouseAction](../reference/arkui-ts/ts-appendix-enums.md#mouseaction))、时间戳(timestamp)、交互组件的区域([EventTarget](../reference/arkui-ts/ts-universal-events-click.md))、事件来源([SourceType](../reference/arkui-ts/ts-gesture-settings.md))等。MouseEvent的回调函数stopPropagation用于设置当前事件是否阻止冒泡。
>**说明:**
>
>按键(MouseButton)的值:Left/Right/Middle/Back/Forward 均对应鼠标上的实体按键,当这些按键被按下或松开时触发这些按键的事件。None表示无按键,会出现在鼠标没有按键按下或松开的状态下,移动鼠标所触发的事件中。
```ts
// xxx.ets
@Entry
@Component
struct MouseExample {
@State isHovered: boolean = false;
@State buttonText: string = '';
@State columnText: string = '';
build() {
Column() {
Button(this.isHovered ? 'Hovered!' : 'Not Hover')
.width(200)
.height(100)
.backgroundColor(this.isHovered ? Color.Green : Color.Gray)
.onHover((isHover: boolean) => {
this.isHovered = isHover
})
.onMouse((event: MouseEvent) => { // 给Button组件设置onMouse回调
this.buttonText = 'Button onMouse:\n' + '' +
'button = ' + event.button + '\n' +
'action = ' + event.action + '\n' +
'x,y = (' + event.x + ',' + event.y + ')' + '\n' +
'screenXY=(' + event.screenX + ',' + event.screenY + ')';
})
Divider()
Text(this.buttonText).fontColor(Color.Green)
Divider()
Text(this.columnText).fontColor(Color.Red)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.borderWidth(2)
.borderColor(Color.Red)
.onMouse((event: MouseEvent) => { // 给Column组件设置onMouse回调
this.columnText = 'Column onMouse:\n' + '' +
'button = ' + event.button + '\n' +
'action = ' + event.action + '\n' +
'x,y = (' + event.x + ',' + event.y + ')' + '\n' +
'screenXY=(' + event.screenX + ',' + event.screenY + ')';
})
}
}
```
在onHover示例的基础上,给Button绑定onMouse接口。在回调中,打印出鼠标事件的button/action等回调参数值。同时,在外层的Column容器上,也做相同的设置。整个过程可以分为以下两个动作:
1. 移动鼠标:当鼠标从Button外部移入Button的过程中,仅触发了Column的onMouse回调;当鼠标移入到Button内部后,由于onMouse事件默认是冒泡的,所以此时会同时响应Column的onMouse回调和Button的onMouse回调。此过程中,由于鼠标仅有移动动作没有点击动作,因此打印信息中的button均为0(MouseButton.None的枚举值)、action均为3(MouseAction.Move的枚举值)。
2. 点击鼠标:鼠标进入Button后进行了2次点击,分别是左键点击和右键点击。
左键点击时:button = 1(MouseButton.Left的枚举值),按下时 action = 1(MouseAction.Press的枚举值),抬起时 action = 2(MouseAction.Release的枚举值)。
右键点击时:button = 2(MouseButton.Right的枚举值),按下时 action = 1(MouseAction.Press的枚举值),抬起时 action = 2(MouseAction.Release的枚举值)。
![onMouse1](figures/onMouse1.gif)
如果需要阻止鼠标事件冒泡,可以通过调用stopPropagation()方法进行设置。
```ts
Button(this.isHovered ? 'Hovered!' : 'Not Hover')
.width(200)
.height(100)
.backgroundColor(this.isHovered ? Color.Green : Color.Gray)
.onHover((isHover: boolean) => {
this.isHovered = isHover;
})
.onMouse((event: MouseEvent) => {
event.stopPropagation(); // 在Button的onMouse事件中设置阻止冒泡
this.buttonText = 'Button onMouse:\n' + '' +
'button = ' + event.button + '\n' +
'action = ' + event.action + '\n' +
'x,y = (' + event.x + ',' + event.y + ')' + '\n' +
'screenXY=(' + event.screenX + ',' + event.screenY + ')';
})
```
在子组件(Button)的onMouse中,通过回调参数event调用stopPropagation回调方法(如下)即可阻止Button子组件的鼠标事件冒泡到父组件Column上。
```ts
event.stopPropagation()
```
效果是:当鼠标在Button组件上操作时,仅Button的onMouse回调会响应,Column的onMouse回调不会响应。
### hoverEffect
```ts
hoverEffect(value: HoverEffect)
```
鼠标悬浮态效果设置的通用属性。参数类型为HoverEffect,HoverEffect提供的Auto、Scale、Highlight效果均为固定效果,开发者无法自定义设置效果参数。
**表1** HoverEffect说明
| HoverEffect枚举值 | 效果说明 |
| -------------- | ---------------------------------------- |
| Auto | 组件默认提供的悬浮态效果,由各组件定义。 |
| Scale | 动画播放方式,鼠标悬浮时:组件大小从100%放大至105%,鼠标离开时:组件大小从105%缩小至100%。 |
| Highlight | 动画播放方式,鼠标悬浮时:组件背景色叠加一个5%透明度的白色,视觉效果是组件的原有背景色变暗,鼠标离开时:组件背景色恢复至原有样式。 |
| None | 禁用悬浮态效果 |
```ts
// xxx.ets
@Entry
@Component
struct HoverExample {
build() {
Column({ space: 10 }) {
Button('Auto')
.width(170).height(70)
Button('Scale')
.width(170).height(70)
.hoverEffect(HoverEffect.Scale)
Button('Highlight')
.width(170).height(70)
.hoverEffect(HoverEffect.Highlight)
Button('None')
.width(170).height(70)
.hoverEffect(HoverEffect.None)
}.width('100%').height('100%').justifyContent(FlexAlign.Center)
}
}
```
![hoverEffect](figures/hoverEffect.gif)
Button默认的悬浮态效果就是缩放效果,因此Auto和Scale的效果一样,Highlight会使背板颜色变暗,None会禁用悬浮态效果。
## 按键事件
**图2** 按键事件数据流  
![zh-cn_image_0000001511580944](figures/zh-cn_image_0000001511580944.png)
按键事件由外设键盘等设备触发,经驱动和多模处理转换后发送给当前获焦的窗口。窗口获取到事件后,会先给输入法分发(输入法会消费按键用作输入),若输入法未消费该按键事件,才会将事件发给ArkUI框架。因此,当某输入框组件获焦,且打开了输入法,此时大部分按键事件均会被输入法消费,例如字母键会被输入法用来往输入框中输入对应字母字符、方向键会被输入法用来切换选中备选词。
按键事件到ArkUI框架之后,会先找到完整的父子节点获焦链。从叶子节点到根节点,逐一发送按键事件。
### onKeyEvent
```ts
onKeyEvent(event: (event?: KeyEvent) => void)
```
按键事件回调,当绑定该方法的组件处于[获焦状态](arkts-common-events-focus-event.md)下,外设键盘的按键事件会触发该API的回调响应,回调参数为[KeyEvent](../reference/arkui-ts/ts-universal-events-key.md),可由该参数获得当前按键事件的按键行为([KeyType](../reference/arkui-ts/ts-appendix-enums.md#keytype))、键码([keyCode](../reference/apis/js-apis-keycode.md))、按键英文名称(keyText)、事件来源设备类型([KeySource](../reference/arkui-ts/ts-appendix-enums.md#keysource))、事件来源设备id(deviceId)、元键按压状态(metaKey)、时间戳(timestamp)、阻止冒泡设置(stopPropagation)。
```ts
// xxx.ets
@Entry
@Component
struct KeyEventExample {
@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;
})
}
}
```
上述示例中给组件Button和其父容器Column绑定onKeyEvent。应用打开页面加载后,组件树上第一个可获焦的非容器组件自动获焦,该应用只有一个Button组件,因此该组件会自动获焦,由于Button是Column的子节点,Button获焦也同时意味着Column获焦。获焦机制见[焦点事件](arkts-common-events-focus-event.md)
![zh-cn_image_0000001511421324](figures/zh-cn_image_0000001511421324.gif)
打开应用后,依次在键盘上按这些按键:“空格、回车、左Ctrl、左Shift、字母A、字母Z”。
1. 由于onKeyEvent事件默认是冒泡的,所以Button和Column的onKeyEvent都可以响应。
2. 每个按键都有2次回调,分别对应KeyType.Down和KeyType.Up,表示按键被按下、然后抬起。
如果要阻止冒泡,即仅Button响应键盘事件,Column不响应,在Button的onKeyEvent回调中加入event.stopPropagation()方法即可,如下:
```ts
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;
})
```
![zh-cn_image_0000001511900508](figures/zh-cn_image_0000001511900508.gif)
# 触屏事件
触屏事件指当手指/手写笔在组件上按下、滑动、抬起时触发的回调事件。包括[点击事件](#点击事件)[拖拽事件](#拖拽事件)[触摸事件](#触摸事件)
**图1 ** 触摸事件原理
![zh-cn_image_0000001562700461](figures/zh-cn_image_0000001562700461.png)
## 点击事件
点击事件是指通过手指或手写笔做出一次完整的按下和抬起动作。当发生点击事件时,会触发以下回调函数:
```ts
onClick(event: (event?: ClickEvent) => void)
```
event参数提供点击事件相对于窗口或组件的坐标位置,以及发生点击的事件源。
例如通过按钮的点击事件控制图片的显示和隐藏。
```ts
@Entry
@Component
struct IfElseTransition {
@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%')
}
}
```
## 拖拽事件
拖拽事件指手指/手写笔长按组件(&gt;=500ms),并拖拽到接收区域释放的事件。拖拽事件触发流程:
![zh-cn_image_0000001562820825](figures/zh-cn_image_0000001562820825.png)
拖拽事件的触发通过长按、拖动平移判定,手指平移的距离达到5vp即可触发拖拽事件。ArkUI支持应用内、跨应用的拖拽事件。
拖拽事件提供以下[接口](../reference/arkui-ts/ts-universal-events-drag-drop.md)
| 接口名称 | 描述 |
| ---------------------------------------- | ---------------------------------------- |
| onDragStart(event:&nbsp;(event?:&nbsp;DragEvent,&nbsp;extraParams?:&nbsp;string)&nbsp;=&gt;&nbsp;CustomBuilder&nbsp;\|&nbsp;DragItemInfo) | 拖拽启动接口。当前仅支持自定义pixelmap和自定义组件。 |
| onDragEnter(event:&nbsp;(event?:&nbsp;DragEvent,&nbsp;extraParams?:&nbsp;string)&nbsp;=&gt;&nbsp;void) | 拖拽进入组件接口。DragEvent定义拖拽发生位置,extraParmas表示用户自定义信息 |
| onDragLeave(event:&nbsp;(event?:&nbsp;DragEvent,&nbsp;extraParams?:&nbsp;string)&nbsp;=&gt;&nbsp;void) | 拖拽离开组件接口。DragEvent定义拖拽发生位置,extraParmas表示拖拽事件额外信息。 |
| onDragMove(event:&nbsp;(event?:&nbsp;DragEvent,&nbsp;extraParams?:&nbsp;string)&nbsp;=&gt;&nbsp;void) | 拖拽移动接口。DragEvent定义拖拽发生位置,extraParmas表示拖拽事件额外信息。 |
| onDrop(event:&nbsp;(event?:&nbsp;DragEvent,&nbsp;extraParams?:&nbsp;string)&nbsp;=&gt;&nbsp;void) | 拖拽释放组件接口。DragEvent定义拖拽发生位置,extraParmas表示拖拽事件额外信息。 |
如下是跨窗口拖拽,拖出窗口示例:
```ts
import image from '@ohos.multimedia.image';
@Entry
@Component
struct Index {
@State text: string = ''
@State bool1: boolean = false
@State bool2: boolean = false
@State visible: Visibility = Visibility.Visible
@State pixelMap: PixelMap = undefined
private pixelMapReader = undefined
aboutToAppear() {
console.info('begin to create pixmap has info message: ')
this.createPixelMap()
}
createPixelMap() {
let color = new ArrayBuffer(4 * 96 * 96);
var buffer = new Uint8Array(color);
for (var i = 0; i < buffer.length; i++) {
buffer[i] = (i + 1) % 255;
}
let opts = {
alphaType: 0,
editable: true,
pixelFormat: 4,
scaleMode: 1,
size: { height: 96, width: 96 }
}
const promise = image.createPixelMap(color, opts);
promise.then((data) => {
console.info('create pixmap has info message: ' + JSON.stringify(data))
this.pixelMap = data;
this.pixelMapReader = data;
})
}
@Builder pixelMapBuilder() {
Text('drag item')
.width('100%')
.height(100)
.fontSize(16)
.textAlign(TextAlign.Center)
.borderRadius(10)
.backgroundColor(0xFFFFFF)
}
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Text('App1')
.width('40%')
.height(80)
.fontSize(20)
.margin(30)
.textAlign(TextAlign.Center)
.backgroundColor(Color.Pink)
.visibility(Visibility.Visible)
Text('Across Window Drag This')
.width('80%')
.height(80)
.fontSize(16)
.margin(30)
.textAlign(TextAlign.Center)
.backgroundColor(Color.Pink)
.visibility(this.visible)
.onDragStart(() => { //启动跨窗口拖拽
console.info('Text onDrag start')
this.bool1 = true
this.text = 'TextDrag'
return { pixelMap: this.pixelMapReader, extraInfo: 'custom extra info.' }
})
.onDrop((event: DragEvent, extraParams: string) => {
console.info('Text onDragDrop, ')
this.visible = Visibility.None //拖动结束后,使源不可见
})
}
.width('100%')
.height('100%')
}
}
```
跨窗口拖拽,拖入示例:
```ts
@Entry
@Component
struct Index {
@State number: string[] = ['drag here']
@State text: string = ''
@State bool1: boolean = false
@State bool2: boolean = false
@State visible: Visibility = Visibility.Visible
@State visible2: Visibility = Visibility.None
scroller: Scroller = new Scroller()
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Text('App2')
.width('40%')
.height(80)
.fontSize(20)
.margin(30)
.textAlign(TextAlign.Center)
.backgroundColor(Color.Pink)
.visibility(Visibility.Visible)
List({ space: 20, initialIndex: 0 }) {
ForEach(this.number, (item) => {
ListItem() {
Text('' + item)
.width('100%')
.height(80)
.fontSize(16)
.borderRadius(10)
.textAlign(TextAlign.Center)
.backgroundColor(0xFFFFFF)
}
}, item => item)
ListItem() {
Text('Across Window Drag This')
.width('80%')
.height(80)
.fontSize(16)
.margin(30)
.textAlign(TextAlign.Center)
.backgroundColor(Color.Pink)
.visibility(this.visible2)
}
}
.height('50%')
.width('90%')
.border({ width: 1 })
.divider({ strokeWidth: 2, color: 0xFFFFFF, startMargin: 20, endMargin: 20 })
.onDragEnter((event: DragEvent, extraParams: string) => { //拖拽进去组件
console.info('List onDragEnter, ' + extraParams)
})
.onDragMove((event: DragEvent, extraParams: string) => { //拖拽时移动
console.info('List onDragMove, ' + extraParams)
})
.onDragLeave((event: DragEvent, extraParams: string) => { //拖拽离开组件
console.info('List onDragLeave, ' + extraParams)
})
.onDrop((event: DragEvent, extraParams: string) => { //释放组件
console.info('List onDragDrop, ' + extraParams)
this.visible2 = Visibility.Visible //拖拽完成使拖入目标可见
})
}
.width('100%')
.height('100%')
}
}
```
## 触摸事件
当手指或手写笔在组件上触碰时,会触发不同动作所对应的事件响应,包括按下(Down)、滑动(Move)、抬起(Up)事件:
```ts
onTouch(event: (event?: TouchEvent) => void)
```
- event.type为TouchType.Down:表示手指按下。
- event.type为TouchType.Up:表示手指抬起。
- event.type为TouchType.Move:表示手指按住移动。
触摸事件可以同时多指触发,通过event参数可获取触发的手指位置、手指唯一标志、当前发生变化的手指和输入的设备源等信息。
```ts
// xxx.ets
@Entry
@Component
struct TouchExample {
@State text: string = '';
@State eventType: string = '';
build() {
Column() {
Button('Touch').height(40).width(100)
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.eventType = 'Down';
}
if (event.type === TouchType.Up) {
this.eventType = 'Up';
}
if (event.type === TouchType.Move) {
this.eventType = 'Move';
}
this.text = 'TouchType:' + this.eventType + '\nDistance between touch point and touch element:\nx: '
+ event.touches[0].x + '\n' + 'y: ' + event.touches[0].y + '\nComponent globalPos:('
+ event.target.area.globalPosition.x + ',' + event.target.area.globalPosition.y + ')\nwidth:'
+ event.target.area.width + '\nheight:' + event.target.area.height
})
Button('Touch').height(50).width(200).margin(20)
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.eventType = 'Down';
}
if (event.type === TouchType.Up) {
this.eventType = 'Up';
}
if (event.type === TouchType.Move) {
this.eventType = 'Move';
}
this.text = 'TouchType:' + this.eventType + '\nDistance between touch point and touch element:\nx: '
+ event.touches[0].x + '\n' + 'y: ' + event.touches[0].y + '\nComponent globalPos:('
+ event.target.area.globalPosition.x + ',' + event.target.area.globalPosition.y + ')\nwidth:'
+ event.target.area.width + '\nheight:' + event.target.area.height
})
Text(this.text)
}.width('100%').padding(30)
}
}
```
![zh-cn_image_0000001511900468](figures/zh-cn_image_0000001511900468.gif)
# 使用画布绘制自定义图形
Canvas提供画布组件,用于自定义绘制图形,开发者使用CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象在Canvas组件上进行绘制,绘制对象可以是基础形状、文本、图片等。
## 使用画布组件绘制自定义图形
可以由以下三种形式在画布绘制自定义图形:
- 使用[CanvasRenderingContext2D对象](../reference/arkui-ts/ts-canvasrenderingcontext2d.md)在Canvas画布上绘制。
```ts
@Entry
@Component
struct CanvasExample1 {
//用来配置CanvasRenderingContext2D对象的参数,包括是否开启抗锯齿,true表明开启抗锯齿。
private settings: RenderingContextSettings = new RenderingContextSettings(true)
//用来创建CanvasRenderingContext2D对象,通过在canvas中调用CanvasRenderingContext2D对象来绘制。
private context: CanvasRenderingContext2D= new CanvasRenderingContext2D(this.settings)
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
//在canvas中调用CanvasRenderingContext2D对象。
Canvas(this.context)
.width('100%')
.height('100%')
.backgroundColor('#F5DC62')
.onReady(() =>{
//可以在这里绘制内容。
this.context.strokeRect(50, 50, 200, 150);
})
}
.width('100%')
.height('100%')
}
}
```
![2023022793003(1)](figures/2023022793003(1).jpg)
- 离屏绘制是指将需要绘制的内容先绘制在缓存区,再将其转换成图片,一次性绘制到Canvas上,加快了绘制速度。过程为:
1. 通过transferToImageBitmap方法将离屏画布最近渲染的图像创建为一个ImageBitmap对象。
2. 通过CanvasRenderingContext2D对象的transferFromImageBitmap方法显示给定的ImageBitmap对象。
具体使用参考[OffscreenCanvasRenderingContext2D对象](../reference/arkui-ts/ts-offscreencanvasrenderingcontext2d.md)。
```ts
@Entry
@Component
struct CanvasExample2 {
//用来配置CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象的参数,包括是否开启抗锯齿。true表明开启抗锯齿
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
//用来创建OffscreenCanvasRenderingContext2D对象,width为离屏画布的宽度,height为离屏画布的高度。通过在canvas中调用OffscreenCanvasRenderingContext2D对象来绘制。
private offContext: OffscreenCanvasRenderingContext2D = new OffscreenCanvasRenderingContext2D(600, 600, this.settings)
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Canvas(this.context)
.width('100%')
.height('100%')
.backgroundColor('#F5DC62')
.onReady(() =>{
//可以在这里绘制内容
this.offContext.strokeRect(50, 50, 200, 150);
//将离屏绘值渲染的图像在普通画布上显示
let image = this.offContext.transferToImageBitmap();
this.context.transferFromImageBitmap(image);
})
}
.width('100%')
.height('100%')
}
}
```
![2023022793003(1)](figures/2023022793003(1).jpg)
>**说明:**
>
>在画布组件中,通过CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象在Canvas组件上进行绘制时调用的接口相同,另接口参数如无特别说明,单位均为vp。
- 在Canvas上加载Lottie动画时,需要先按照如下方式下载Lottie。
```ts
import lottie from '@ohos/lottie'
```
具体接口参考[Lottie](../reference/arkui-ts/ts-components-canvas-lottie.md),相关实例请参考[Lottie实例](https://gitee.com/openharmony/applications_app_samples/tree/master/ETSUI/Lottie)
>**说明:**
>
>在第一次使用Lottie之前,需要在Terminal窗口运行ohpm install \@ohos/lottieETS命令下载Lottie。
## 初始化画布组件
onReady(event: () =&gt; void)是Canvas组件初始化完成时的事件回调,调用该事件后,可获取Canvas组件的确定宽高,进一步使用CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象调用相关API进行图形绘制。
```ts
Canvas(this.context)
.width('100%')
.height('100%')
.backgroundColor('#F5DC62')
.onReady(() => {
this.context.fillStyle = '#0097D4';
this.context.fillRect(50, 50, 100, 100);
})
```
![2023022793350(1)](figures/2023022793350(1).jpg)
## 画布组件绘制方式
在Canvas组件生命周期接口onReady()调用之后,开发者可以直接使用canvas组件进行绘制。或者可以脱离Canvas组件和onready生命周期,单独定义Path2d对象构造理想的路径,并在onready调用之后使用Canvas组件进行绘制。
- 通过CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象直接调用相关API进行绘制。
```ts
Canvas(this.context)
.width('100%')
.height('100%')
.backgroundColor('#F5DC62')
.onReady(() =>{
this.context.beginPath();
this.context.moveTo(50, 50);
this.context.lineTo(280, 160);
this.context.stroke();
})
```
![2023022793719(1)](figures/2023022793719(1).jpg)
- 先单独定义path2d对象构造理想的路径,再通过调用CanvasRenderingContext2D对象和OffscreenCanvasRenderingContext2D对象的stroke接口或者fill接口进行绘制,具体使用可以参考[Path2D对象](../reference/arkui-ts/ts-components-canvas-path2d.md)
```ts
Canvas(this.context)
.width('100%')
.height('100%')
.backgroundColor('#F5DC62')
.onReady(() =>{
let region = new Path2D();
region.arc(100, 75, 50, 0, 6.28);
this.context.stroke(region);
})
```
![2023022794031(1)](figures/2023022794031(1).jpg)
## 画布组件常用方法
OffscreenCanvasRenderingContext2D对象和CanvasRenderingContext2D对象提供了大量的属性和方法,可以用来绘制文本、图形,处理像素等,是Canvas组件的核心。常用接口有[fill](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#fill)(对封闭路径进行填充)、[clip](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#clip)(设置当前路径为剪切路径)[stroke](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#stroke)(进行边框绘制操作)等等,同时提供了[fillStyle](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#fillstyle)(指定绘制的填充色)、[globalAlpha](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#globalalpha)(设置透明度)与[strokeStyle](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#strokestyle)(设置描边的颜色)等属性修改绘制内容的样式。将通过以下几个方面简单介绍画布组件常见使用方法:
- 基础形状绘制。
可以通过[arc](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#arc)(绘制弧线路径)、 [ellipse](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#ellipse)(绘制一个椭圆)、[rect](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#rect)(创建矩形路径)等接口绘制基础形状。
```ts
Canvas(this.context)
.width('100%')
.height('100%')
.backgroundColor('#F5DC62')
.onReady(() =>{
//绘制矩形
this.context.beginPath();
this.context.rect(100, 50, 100, 100);
this.context.stroke();
//绘制圆形
this.context.beginPath();
this.context.arc(150, 250, 50, 0, 6.28);
this.context.stroke();
//绘制椭圆
this.context.beginPath();
this.context.ellipse(150, 450, 50, 100, Math.PI * 0.25, Math.PI * 0, Math.PI * 2);
this.context.stroke();
})
```
![2023022794521(1)](figures/2023022794521(1).jpg)
- 文本绘制。
可以通过[fillText](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#filltext)(绘制填充类文本)、[strokeText](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#stroketext)(绘制描边类文本)等接口进行文本绘制。
```ts
Canvas(this.context)
.width('100%')
.height('100%')
.backgroundColor('#F5DC62')
.onReady(() =>{
//绘制填充类文本
this.context.font = '50px sans-serif';
this.context.fillText("Hello World!", 50, 100);
//绘制描边类文本
this.context.font = '55px sans-serif';
this.context.strokeText("Hello World!", 50, 150);
})
```
![2023022795105(1)](figures/2023022795105(1).jpg)
- 绘制图片和图像像素信息处理。
可以通过[drawImage](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#drawimage)(图像绘制)、[putImageData](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#putimagedata)(使用[ImageData](../reference/arkui-ts/ts-components-canvas-imagedata.md)数据填充新的矩形区域)等接口绘制图片,通过[createImageData](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#createimagedata)(创建新的ImageData 对象)、[getPixelMap](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#getpixelmap)(以当前canvas指定区域内的像素创建[PixelMap](../reference/apis/js-apis-image.md#pixelmap7)对象)、[getImageData](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#getimagedata)(以当前canvas指定区域内的像素创建ImageData对象)等接口进行图像像素信息处理。
```ts
@Entry
@Component
struct GetImageData {
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private offContext: OffscreenCanvasRenderingContext2D = new OffscreenCanvasRenderingContext2D(600, 600, this.settings)
private img:ImageBitmap = new ImageBitmap("/common/images/1234.png")
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Canvas(this.context)
.width('100%')
.height('100%')
.backgroundColor('#F5DC62')
.onReady(() =>{
// 使用drawImage接口将图片画在(0,0)为起点,宽高130的区域
this.offContext.drawImage(this.img,0,0,130,130);
// 使用getImageData接口,获得canvas组件区域中,(50,50)为起点,宽高130范围内的绘制内容
let imagedata = this.offContext.getImageData(50,50,130,130);
// 使用putImageData接口将得到的ImageData画在起点为(150, 150)的区域中
this.offContext.putImageData(imagedata,150,150);
// 将离屏绘制的内容画到canvas组件上
let image = this.offContext.transferToImageBitmap();
this.context.transferFromImageBitmap(image);
})
}
.width('100%')
.height('100%')
}
}
```
![drawimage](figures/drawimage.PNG)
- 其他方法。
Canvas中还提供其他类型的方法。渐变([CanvasGradient对象](../reference/arkui-ts/ts-components-canvas-canvasgradient.md))相关的方法:[createLinearGradient](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#createlineargradient)(创建一个线性渐变色)、[createRadialGradient](../reference/arkui-ts/ts-canvasrenderingcontext2d.md#createradialgradient)(创建一个径向渐变色)等。
```ts
Canvas(this.context)
.width('100%')
.height('100%')
.backgroundColor('#F5DC62')
.onReady(() =>{
//创建一个径向渐变色的CanvasGradient对象
let grad = this.context.createRadialGradient(200,200,50, 200,200,200)
//为CanvasGradient对象设置渐变断点值,包括偏移和颜色
grad.addColorStop(0.0, '#E87361');
grad.addColorStop(0.5, '#FFFFF0');
grad.addColorStop(1.0, '#BDDB69');
//用CanvasGradient对象填充矩形
this.context.fillStyle = grad;
this.context.fillRect(0, 0, 400, 400);
})
```
![2023022700701(1)](figures/2023022700701(1).jpg)
## 场景示例
- 规则基础形状绘制:
```ts
@Entry
@Component
struct ClearRect {
private settings: RenderingContextSettings = new RenderingContextSettings(true);
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Canvas(this.context)
.width('100%')
.height('100%')
.backgroundColor('#F5DC62')
.onReady(() =>{
// 设定填充样式,填充颜色设为蓝色
this.context.fillStyle = '#0097D4';
// 以(50, 50)为左上顶点,画一个宽高200的矩形
this.context.fillRect(50,50,200,200);
// 以(70, 70)为左上顶点,清除宽150高100的区域
this.context.clearRect(70,70,150,100);
})
}
.width('100%')
.height('100%')
}
}
```
![2023022701120(1)](figures/2023022701120(1).jpg)
- 不规则图形绘制。
```ts
@Entry
@Component
struct Path2d {
private settings: RenderingContextSettings = new RenderingContextSettings(true);
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
build() {
Row() {
Column() {
Canvas(this.context)
.width('100%')
.height('100%')
.backgroundColor('#F5DC62')
.onReady(() =>{
// 使用Path2D的接口构造一个五边形
let path = new Path2D();
path.moveTo(150, 50);
path.lineTo(50, 150);
path.lineTo(100, 250);
path.lineTo(200, 250);
path.lineTo(250, 150);
path.closePath();
// 设定填充色为蓝色
this.context.fillStyle = '#0097D4';
// 使用填充的方式,将Path2D描述的五边形绘制在canvas组件内部
this.context.fill(path);
})
}
.width('100%')
}
.height('100%')
}
}
```
![2023032422159](figures/2023032422159.jpg)
\ No newline at end of file
# 概述
交互事件按照触发类型来分类,包括触屏事件、键鼠事件和焦点事件。
- [触屏事件](arkts-common-events-touch-screen-event.md):手指或手写笔在触屏上的单指或单笔操作。
- [键鼠事件](arkts-common-events-device-input-event.md):包括外设鼠标或触控板的操作事件和外设键盘的按键事件。
- 鼠标事件是指通过连接和使用外设鼠标/触控板操作时所响应的事件。
- 按键事件是指通过连接和使用外设键盘操作时所响应的事件。
- [焦点事件](arkts-common-events-focus-event.md):通过以上方式控制组件焦点的能力和响应的事件。
手势事件由绑定手势方法和绑定的手势组成,绑定的手势可以分为单一手势和组合手势两种类型,根据手势的复杂程度进行区分。
- [绑定手势方法](arkts-gesture-events-binding.md):用于在组件上绑定单一手势或组合手势,并声明所绑定的手势的响应优先级。
- [单一手势](arkts-gesture-events-single-gesture.md):手势的基本单元,是所有复杂手势的组成部分。
- [组合手势](arkts-gesture-events-combined-gestures.md):由多个单一手势组合而成,可以根据声明的类型将多个单一手势按照一定规则组合成组合手势,并进行使用。
# 绘制几何图形
绘制组件用于在页面绘制图形,Shape组件是绘制组件的父组件,父组件中会描述所有绘制组件均支持的通用属性。具体用法请参考[Shape](../reference/arkui-ts/ts-drawing-components-shape.md)
## 创建绘制组件
绘制组件可以由以下两种形式创建:
- 绘制组件使用Shape作为父组件,实现类似SVG的效果。接口调用为以下形式:
```ts
Shape(value?: PixelMap)
```
该接口用于创建带有父组件的绘制组件,其中value用于设置绘制目标,可将图形绘制在指定的PixelMap对象中,若未设置,则在当前绘制目标中进行绘制。
```ts
Shape() {
Rect().width(300).height(50)
}
```
- 绘制组件单独使用,用于在页面上绘制指定的图形。有7种绘制类型,分别为[Circle](../reference/arkui-ts/ts-drawing-components-circle.md)(圆形)、[Ellipse](../reference/arkui-ts/ts-drawing-components-ellipse.md)(椭圆形)、[Line](../reference/arkui-ts/ts-drawing-components-line.md)(直线)、[Polyine](../reference/arkui-ts/ts-drawing-components-polyline.md)(折线)、[Polygon](../reference/arkui-ts/ts-drawing-components-polygon.md)(多边形)、[Path](../reference/arkui-ts/ts-drawing-components-path.md)(路径)、[Rect](../reference/arkui-ts/ts-drawing-components-rect.md)(矩形)。以Circle的接口调用为例:
```ts
Circle(options?: {width?: string | number, height?: string | number}
```
该接口用于在页面绘制圆形,其中width用于设置圆形的宽度,height用于设置圆形的高度,圆形直径由宽高最小值确定。
```ts
Circle({ width: 150, height: 150 })
```
![创建2](figures/创建2.jpg)
## 形状视口viewport
```ts
viewPort{ x?: number | string, y?: number | string, width?: number | string, height?: number | string }
```
形状视口viewport指定用户空间中的一个矩形,该矩形映射到为关联的 SVG 元素建立的视区边界。viewport属性的值包含x、y、width和height四个可选参数,x 和 y 表示视区的左上角坐标,width和height表示其尺寸。
以下3个示例讲解Viewport具体用法:
- 通过形状视口对图形进行放大与缩小。
```ts
// 画一个宽高都为150的圆
Text('原始尺寸Circle组件')
Circle({width: 75, height: 75}).fill('#E87361')
Row({space:10}) {
Column() {
// 创建一个宽高都为150的shape组件,背景色为黄色,一个宽高都为75的viewport。用一个蓝色的矩形来填充viewport,在viewport中绘制一个直径为75的圆。
// 绘制结束,viewport会根据组件宽高放大两倍
Text('shape内放大的Circle组件')
Shape() {
Rect().width('100%').height('100%').fill('#0097D4')
Circle({width: 75, height: 75}).fill('#E87361')
}
.viewPort({x: 0, y: 0, width: 75, height: 75})
.width(150)
.height(150)
.backgroundColor('#F5DC62')
}
Column() {
// 创建一个宽高都为150的shape组件,背景色为黄色,一个宽高都为300的viewport。用一个绿色的矩形来填充viewport,在viewport中绘制一个直径为75的圆。
// 绘制结束,viewport会根据组件宽高缩小两倍。
Text('Shape内缩小的Circle组件')
Shape() {
Rect().width('100%').height('100%').fill('#BDDB69')
Circle({width: 75, height: 75}).fill('#E87361')
}
.viewPort({x: 0, y: 0, width: 300, height: 300})
.width(150)
.height(150)
.backgroundColor('#F5DC62')
}
}
```
![2023032401632](figures/2023032401632.jpg)
- 创建一个宽高都为300的shape组件,背景色为黄色,一个宽高都为300的viewport。用一个蓝色的矩形来填充viewport,在viewport中绘制一个半径为75的圆。
```ts
Shape() {
Rect().width("100%").height("100%").fill("#0097D4")
Circle({ width: 150, height: 150 }).fill("#E87361")
}
.viewPort({ x: 0, y: 0, width: 300, height: 300 })
.width(300)
.height(300)
.backgroundColor("#F5DC62")
```
![viewport(2)](figures/viewport(2).jpg)
- 创建一个宽高都为300的shape组件,背景色为黄色,创建一个宽高都为300的viewport。用一个蓝色的矩形来填充viewport,在viewport中绘制一个半径为75的圆,将viewport向右方和下方各平移150。
```ts
Shape() {
Rect().width("100%").height("100%").fill("#0097D4")
Circle({ width: 150, height: 150 }).fill("#E87361")
}
.viewPort({ x: -150, y: -150, width: 300, height: 300 })
.width(300)
.height(300)
.backgroundColor("#F5DC62")
```
![viewport(3)](figures/viewport(3).jpg)
## 自定义样式
绘制组件支持通过各种属性对组件样式进行更改。
- 通过fill可以设置组件填充区域颜色。
```ts
Path()
.width(100)
.height(100)
.commands('M150 0 L300 300 L0 300 Z')
.fill("#E87361")
```
![2023022792216(1)](figures/2023022792216(1).jpg)
- 通过stroke可以设置组件边框颜色。
```ts
Path()
.width(100)
.height(100)
.fillOpacity(0)
.commands('M150 0 L300 300 L0 300 Z')
.stroke(Color.Red)
```
![stroke](figures/stroke.jpg)
- 通过strokeOpacity可以设置边框透明度。
```ts
Path()
.width(100)
.height(100)
.fillOpacity(0)
.commands('M150 0 L300 300 L0 300 Z')
.stroke(Color.Red)
.strokeWidth(10)
.strokeOpacity(0.2)
```
![strokeopacity](figures/strokeopacity.jpg)
- 通过strokeLineJoin可以设置线条拐角绘制样式。拐角绘制样式分为Bevel(使用斜角连接路径段)、Miter(使用尖角连接路径段)、Round(使用圆角连接路径段)。
```ts
Polyline()
.width(100)
.height(100)
.fillOpacity(0)
.stroke(Color.Red)
.strokeWidth(8)
.points([[20, 0], [0, 100], [100, 90]])
// 设置折线拐角处为圆弧
.strokeLineJoin(LineJoinStyle.Round)
```
![strokeLineJoin](figures/strokeLineJoin.jpg)
- 通过strokeMiterLimit设置斜接长度与边框宽度比值的极限值。
斜接长度表示外边框外边交点到内边交点的距离,边框宽度即strokeWidth属性的值。strokeMiterLimit取值需大于等于1,且在strokeLineJoin属性取值LineJoinStyle.Miter时生效。
```ts
Polyline()
.width(100)
.height(100)
.fillOpacity(0)
.stroke(Color.Red)
.strokeWidth(10)
.points([[20, 0], [20, 100], [100, 100]])
// 设置折线拐角处为尖角
.strokeLineJoin(LineJoinStyle.Miter)
// 设置斜接长度与线宽的比值
.strokeMiterLimit(1/Math.sin(45))
Polyline()
.width(100)
.height(100)
.fillOpacity(0)
.stroke(Color.Red)
.strokeWidth(10)
.points([[20, 0], [20, 100], [100, 100]])
.strokeLineJoin(LineJoinStyle.Miter)
.strokeMiterLimit(1.42)
```
![2023032405917](figures/2023032405917.jpg)
- 通过antiAlias设置是否开启抗锯齿,默认值为true(开启抗锯齿)。
```ts
//开启抗锯齿
Circle()
.width(150)
.height(200)
.fillOpacity(0)
.strokeWidth(5)
.stroke(Color.Black)
```
![无标题](figures/无标题.png)
```ts
//关闭抗锯齿
Circle()
.width(150)
.height(200)
.fillOpacity(0)
.strokeWidth(5)
.stroke(Color.Black)
.antiAlias(false)
```
![2023032411518](figures/2023032411518.jpg)
## 场景示例
- 在Shape的(-80, -5)点绘制一个封闭路径,填充颜色0x317AF7,线条宽度10,边框颜色红色,拐角样式锐角(默认值)。
```ts
@Entry
@Component
struct ShapeExample {
build() {
Column({ space: 10 }) {
Shape() {
Path().width(200).height(60).commands('M0 0 L400 0 L400 150 Z')
}
.viewPort({ x: -80, y: -5, width: 500, height: 300 })
.fill(0x317AF7)
.stroke(Color.Red)
.strokeWidth(3)
.strokeLineJoin(LineJoinStyle.Miter)
.strokeMiterLimit(5)
}.width('100%').margin({ top: 15 })
}
}
```
![场景1](figures/场景1.jpg)
- 绘制一个直径为150的圆,和一个直径为150、线条为红色虚线的圆环(宽高设置不一致时以短边为直径)。
```ts
@Entry
@Component
struct CircleExample {
build() {
Column({ space: 10 }) {
//绘制一个直径为150的圆
Circle({ width: 150, height: 150 })
//绘制一个直径为150、线条为红色虚线的圆环
Circle()
.width(150)
.height(200)
.fillOpacity(0)
.strokeWidth(3)
.stroke(Color.Red)
.strokeDashArray([1, 2])
}.width('100%')
}
}
```
![场景2](figures/场景2.jpg)
# 绑定手势方法
通过给各个组件绑定不同的手势事件,并设计事件的响应方式,当手势识别成功时,ArkUI框架将通过事件回调通知组件手势识别的结果。
## gesture(常规手势绑定方法)
```ts
.gesture(gesture: GestureType, mask?: GestureMask)
```
gesture为通用的一种手势绑定方法,可以将手势绑定到对应的组件上。
例如,可以将点击手势TapGesture通过gesture手势绑定方法绑定到Text组件上。
```ts
// xxx.ets
@Entry
@Component
struct Index {
build() {
Column() {
Text('Gesture').fontSize(28)
// 采用gesture手势绑定方法绑定TapGesture
.gesture(
TapGesture()
.onAction(() => {
console.info('TapGesture is onAction');
}))
}
.height(200)
.width(250)
}
}
```
## priorityGesture(带优先级的手势绑定方法)
```ts
.priorityGesture(gesture: GestureType, mask?: GestureMask)
```
priorityGesture是带优先级的手势绑定方法,可以在组件上绑定优先识别的手势。
在默认情况下,当父组件和子组件使用gesture绑定同类型的手势时,子组件优先识别通过gesture绑定的手势。当父组件使用priorityGesture绑定与子组件同类型的手势时,父组件优先识别通过priorityGesture绑定的手势。
例如,当父组件Column和子组件Text同时绑定TapGesture手势时,父组件以带优先级手势priorityGesture的形式进行绑定时,优先响应父组件绑定的TapGesture。
```ts
// xxx.ets
@Entry
@Component
struct Index {
build() {
Column() {
Text('Gesture').fontSize(28)
.gesture(
TapGesture()
.onAction(() => {
console.info('Text TapGesture is onAction');
}))
}
.height(200)
.width(250)
// 设置为priorityGesture时,点击文本区域会忽略Text组件的TapGesture手势事件,优先响应父组件Column的TapGesture手势事件
.priorityGesture(
TapGesture()
.onAction(() => {
console.info('Column TapGesture is onAction');
}), GestureMask.IgnoreInternal)
}
}
```
## parallelGesture(并行手势绑定方法)
```ts
.parallelGesture(gesture: GestureType, mask?: GestureMask)
```
parallelGesture是并行的手势绑定方法,可以在父子组件上绑定可以同时响应的相同手势。
在默认情况下,手势事件为非冒泡事件,当父子组件绑定相同的手势时,父子组件绑定的手势事件会发生竞争,最多只有一个组件的手势事件能够获得响应。而当父组件绑定了并行手势parallelGesture时,父子组件相同的手势事件都可以触发,实现类似冒泡效果。
```ts
// xxx.ets
@Entry
@Component
struct Index {
build() {
Column() {
Text('Gesture').fontSize(28)
.gesture(
TapGesture()
.onAction(() => {
console.info('Text TapGesture is onAction');
}))
}
.height(200)
.width(250)
// 设置为parallelGesture时,点击文本区域会同时响应父组件Column和子组件Text的TapGesture手势事件
.parallelGesture(
TapGesture()
.onAction(() => {
console.info('Column TapGesture is onAction');
}), GestureMask.IgnoreInternal)
}
}
```
>**说明:**
>
>当父组件和子组件同时绑定单击手势事件和双击手势事件时,父组件和子组件均只响应单击手势事件。
# 组合手势
组合手势由多种单一手势组合而成,通过在GestureGroup中使用不同的GestureMode来声明该组合手势的类型,支持[连续识别](#连续识别)[并行识别](#并行识别)[互斥识别](#互斥识别)三种类型。
```ts
GestureGroup(mode:GestureMode, ...gesture:GestureType[])
```
- mode:必选参数,为GestureMode枚举类。用于声明该组合手势的类型。
- gesture:必选参数,为由多个手势组合而成的数组。用于声明组合成该组合手势的各个手势。
## 连续识别
连续识别组合手势对应的GestureMode为Sequence。连续识别组合手势将按照手势的注册顺序识别手势,直到所有的手势识别成功。当连续识别组合手势中有一个手势识别失败时,所有的手势识别失败。
以一个由长按手势和拖动手势组合而成的连续手势为例:
在一个Column组件上绑定了translate属性,通过修改该属性可以设置组件的位置移动。然后在该组件上绑定LongPressGesture和PanGesture组合而成的Sequence组合手势。当触发LongPressGesture时,更新显示的数字。当长按后进行拖动时,根据拖动手势的回调函数,实现组件的拖动。
```ts
// xxx.ets
@Entry
@Component
struct Index {
@State offsetX: number = 0;
@State offsetY: number = 0;
@State count: number = 0;
@State positionX: number = 0;
@State positionY: number = 0;
@State borderStyles: BorderStyle = BorderStyle.Solid
build() {
Column() {
Text('sequence gesture\n' + 'LongPress onAction:' + this.count + '\nPanGesture offset:\nX: ' + this.offsetX + '\n' + 'Y: ' + this.offsetY)
.fontSize(28)
}
// 绑定translate属性可以实现组件的位置移动
.translate({ x: this.offsetX, y: this.offsetY, z: 0 })
.height(250)
.width(300)
//以下组合手势为顺序识别,当长按手势事件未正常触发时不会触发拖动手势事件
.gesture(
// 声明该组合手势的类型为Sequence类型
GestureGroup(GestureMode.Sequence,
// 该组合手势第一个触发的手势为长按手势,且长按手势可多次响应
LongPressGesture({ repeat: true })
// 当长按手势识别成功,增加Text组件上显示的count次数
.onAction((event: GestureEvent) => {
if (event.repeat) {
this.count++;
}
console.info('LongPress onAction');
})
.onActionEnd(() => {
console.info('LongPress end');
}),
// 当长按之后进行拖动,PanGesture手势被触发
PanGesture()
.onActionStart(() => {
this.borderStyles = BorderStyle.Dashed;
console.info('pan start');
})
// 当该手势被触发时,根据回调获得拖动的距离,修改该组件的位移距离从而实现组件的移动
.onActionUpdate((event: GestureEvent) => {
this.offsetX = this.positionX + event.offsetX;
this.offsetY = this.positionY + event.offsetY;
console.info('pan update');
})
.onActionEnd(() => {
this.positionX = this.offsetX;
this.positionY = this.offsetY;
this.borderStyles = BorderStyle.Solid;
})
)
)
}
}
```
![sequence](figures/sequence.gif)
>**说明:**
>
>拖拽事件是一种典型的连续识别组合手势事件,由长按手势事件和滑动手势事件组合而成。只有先长按达到长按手势事件预设置的时间后进行滑动才会触发拖拽事件。如果长按事件未达到或者长按后未进行滑动,拖拽事件均识别失败。
## 并行识别
并行识别组合手势对应的GestureMode为Parallel。并行识别组合手势中注册的手势将同时进行识别,直到所有手势识别结束。并行识别手势组合中的手势进行识别时互不影响。
以在一个Column组件上绑定点击手势和双击手势组成的并行识别手势为例,由于单击手势和双击手势是并行识别,因此两个手势可以同时进行识别,二者互不干涉。
```ts
// xxx.ets
@Entry
@Component
struct Index {
@State count1: number = 0;
@State count2: number = 0;
build() {
Column() {
Text('parallel gesture\n' + 'tapGesture count is 1:' + this.count1 + '\ntapGesture count is 2:' + this.count2 + '\n')
.fontSize(28)
}
.height(200)
.width(250)
// 以下组合手势为并行并别,单击手势识别成功后,若在规定时间内再次点击,双击手势也会识别成功
.gesture(
GestureGroup(GestureMode.Parallel,
TapGesture({ count: 1 })
.onAction(() => {
this.count1++;
}),
TapGesture({ count: 2 })
.onAction(() => {
this.count2++;
})
)
)
}
}
```
![parallel](figures/parallel.gif)
>**说明:**
>
>当由单击手势和双击手势组成一个并行识别组合手势后,在区域内进行点击时,单击手势和双击手势将同时进行识别。
>
>当只有单次点击时,单击手势识别成功,双击手势识别失败。
>
>当有两次点击时,若两次点击相距时间在规定时间内(默认规定时间为300毫秒),触发两次单击事件和一次双击事件。
>
>当有两次点击时,若两次点击相距时间超出规定时间,触发两次单击事件不触发双击事件。
## 互斥识别
互斥识别组合手势对应的GestureMode为Exclusive。互斥识别组合手势中注册的手势将同时进行识别,若有一个手势识别成功,则结束手势识别,其他所有手势识别失败。
以在一个Column组件上绑定单击手势和双击手势组合而成的互斥识别组合手势为例,由于单击手势只需要一次点击即可触发而双击手势需要两次,每次的点击事件均被单击手势消费而不能积累成双击手势,所以双击手势无法触发。
```ts
// xxx.ets
@Entry
@Component
struct Index {
@State count1: number = 0;
@State count2: number = 0;
build() {
Column() {
Text('parallel gesture\n' + 'tapGesture count is 1:' + this.count1 + '\ntapGesture count is 2:' + this.count2 + '\n')
.fontSize(28)
}
.height(200)
.width(250)
//以下组合手势为互斥并别,单击手势识别成功后,双击手势会识别失败
.gesture(
GestureGroup(GestureMode.Exclusive,
TapGesture({ count: 1 })
.onAction(() => {
this.count1++;
}),
TapGesture({ count: 2 })
.onAction(() => {
this.count2++;
})
)
)
}
}
```
![exclusive](figures/exclusive.gif)
>**说明:**
>
>当由单击手势和双击手势组成一个互斥识别组合手势后,在区域内进行点击时,单击手势和双击手势将同时进行识别。
>
>当只有单次点击时,单击手势识别成功,双击手势识别失败。
>
>当有两次点击时,单击手势在第一次点击时即宣告识别成功,此时双击手势已经失败。即使在规定时间内进行了第二次点击,双击手势事件也不会进行响应,此时会触发单击手势事件的第二次识别成功。
此差异已折叠。
# 媒体查询
## 概述
[媒体查询](https://docs.openharmony.cn/pages/v3.2Beta/zh-cn/application-dev/reference/apis/js-apis-mediaquery.md/)作为响应式设计的核心,在移动设备上应用十分广泛。媒体查询可根据不同设备类型或同设备不同状态修改应用的样式。媒体查询常用于下面两种场景:
1. 针对设备和应用的属性信息(比如显示区域、深浅色、分辨率),设计出相匹配的布局。
2. 当屏幕发生动态改变时(比如分屏、横竖屏切换),同步更新应用的页面布局。
## 引入与使用流程
媒体查询通过mediaquery模块接口,设置查询条件并绑定回调函数,在对应的条件的回调函数里更改页面布局或者实现业务逻辑,实现页面的响应式设计。具体步骤如下:
首先导入媒体查询模块。
```ts
import mediaquery from '@ohos.mediaquery';
```
通过matchMediaSync接口设置媒体查询条件,保存返回的条件监听句柄listener。例如监听横屏事件:
```ts
let listener = mediaquery.matchMediaSync('(orientation: landscape)');
```
给条件监听句柄listener绑定回调函数onPortrait,当listener检测设备状态变化时执行回调函数。在回调函数内,根据不同设备状态更改页面布局或者实现业务逻辑。
```ts
onPortrait(mediaQueryResult) {
if (mediaQueryResult.matches) {
// do something here
} else {
// do something here
}
}
listener.on('change', onPortrait);
```
## 媒体查询条件
媒体查询条件由媒体类型、逻辑操作符、媒体特征组成,其中媒体类型可省略,逻辑操作符用于连接不同媒体类型与媒体特征,其中,媒体特征要使用“()”包裹且可以有多个。具体规则如下:
### 语法规则
语法规则包括[媒体类型(media-type)](#媒体类型media-type)[媒体逻辑操作(media-logic-operations)](#媒体逻辑操作media-logic-operations)[媒体特征(media-feature)](#媒体特征media-feature)
```ts
[media-type] [media-logic-operations] [(media-feature)]
```
例如:
- screen and (round-screen: true) :表示当设备屏幕是圆形时条件成立。
- (max-height: 800) :表示当高度小于等于800时条件成立。
- (height &lt;= 800) :表示当高度小于等于800时条件成立。
- screen and (device-type: tv) or (resolution &lt; 2) :表示包含多个媒体特征的多条件复杂语句查询,当设备类型为tv或设备分辨率小于2时条件成立。
### 媒体类型(media-type)
| **类型** | **说明** |
| ------ | -------------- |
| screen | 按屏幕相关参数进行媒体查询。 |
### 媒体逻辑操作(media-logic-operations)
媒体逻辑操作符:and、or、not、only用于构成复杂媒体查询,也可以通过comma(, )将其组合起来,详细解释说明如下表。
**表1** 媒体逻辑操作符
| 类型 | 说明 |
| -------------- | ---------------------------------------- |
| and | 将多个媒体特征(Media&nbsp;Feature)以“与”的方式连接成一个媒体查询,只有当所有媒体特征都为true,查询条件成立。另外,它还可以将媒体类型和媒体功能结合起来。例如:screen&nbsp;and&nbsp;(device-type:&nbsp;wearable)&nbsp;and&nbsp;(max-height:&nbsp;600)&nbsp;表示当设备类型是智能穿戴且应用的最大高度小于等于600个像素单位时成立。 |
| or | 将多个媒体特征以“或”的方式连接成一个媒体查询,如果存在结果为true的媒体特征,则查询条件成立。例如:screen&nbsp;and&nbsp;(max-height:&nbsp;1000)&nbsp;or&nbsp;(round-screen:&nbsp;true)&nbsp;表示当应用高度小于等于1000个像素单位或者设备屏幕是圆形时,条件成立。 |
| not | 取反媒体查询结果,媒体查询结果不成立时返回true,否则返回false。例如:not&nbsp;screen&nbsp;and&nbsp;(min-height:&nbsp;50)&nbsp;and&nbsp;(max-height:&nbsp;600)&nbsp;表示当应用高度小于50个像素单位或者大于600个像素单位时成立。<br/>使用not运算符时必须指定媒体类型。 |
| only | 当整个表达式都匹配时,才会应用选择的样式,可以应用在防止某些较早的版本的浏览器上产生歧义的场景。一些较早版本的浏览器对于同时包含了媒体类型和媒体特征的语句会产生歧义,比如:screen&nbsp;and&nbsp;(min-height:&nbsp;50)。老版本浏览器会将这句话理解成screen,从而导致仅仅匹配到媒体类型(screen),就应用了指定样式,使用only可以很好地规避这种情况。<br/>使用only时必须指定媒体类型。 |
| comma(,&nbsp;) | 将多个媒体特征以“或”的方式连接成一个媒体查询,如果存在结果为true的媒体特征,则查询条件成立。其效果等同于or运算符。例如:screen&nbsp;and&nbsp;(min-height:&nbsp;1000),&nbsp;(round-screen:&nbsp;true)&nbsp;表示当应用高度大于等于1000个像素单位或者设备屏幕是圆形时,条件成立。 |
媒体范围操作符包括&lt;=,&gt;=,&lt;&gt;,详细解释说明如下表。
**表2** 媒体逻辑范围操作符
| 类型 | 说明 |
| ----- | ---------------------------------------- |
| &lt;= | 小于等于,例如:screen&nbsp;and&nbsp;(height&nbsp;&lt;=&nbsp;50)。 |
| &gt;= | 大于等于,例如:screen&nbsp;and&nbsp;(height&nbsp;&gt;=&nbsp;600)。 |
| &lt; | 小于,例如:screen&nbsp;and&nbsp;(height&nbsp;&lt;&nbsp;50)。 |
| &gt; | 大于,例如:screen&nbsp;and&nbsp;(height&nbsp;&gt;&nbsp;600)。 |
### 媒体特征(media-feature)
媒体特征包括应用显示区域的宽高、设备分辨率以及设备的宽高等属性,详细说明如下表。
**表3** 媒体特征说明表
| 类型 | 说明 |
| ----------------- | ---------------------------------------- |
| height | 应用页面显示区域的高度。 |
| min-height | 应用页面显示区域的最小高度。 |
| max-height | 应用页面显示区域的最大高度。 |
| width | 应用页面显示区域的宽度。 |
| min-width | 应用页面显示区域的最小宽度。 |
| max-width | 应用页面显示区域的最大宽度。 |
| resolution | 设备的分辨率,支持dpi,dppx和dpcm单位。其中:<br/>-&nbsp;dpi表示每英寸中物理像素个数,1dpi&nbsp;&nbsp;0.39dpcm;<br/>-&nbsp;dpcm表示每厘米上的物理像素个数,1dpcm&nbsp;&nbsp;2.54dpi;<br/>-&nbsp;dppx表示每个px中的物理像素数(此单位按96px&nbsp;=&nbsp;1英寸为基准,与页面中的px单位计算方式不同),1dppx&nbsp;=&nbsp;96dpi。 |
| min-resolution | 设备的最小分辨率。 |
| max-resolution | 设备的最大分辨率。 |
| orientation | 屏幕的方向。<br/>可选值:<br/>-&nbsp;&nbsp;orientation:&nbsp;&nbsp;portrait(设备竖屏);<br/>-&nbsp;&nbsp;orientation:&nbsp;&nbsp;landscape(设备横屏)。 |
| device-height | 设备的高度。 |
| min-device-height | 设备的最小高度。 |
| max-device-height | 设备的最大高度。 |
| device-width | 设备的宽度。 |
| device-type | 设备的类型。<br/>可选值:default、tablet。 |
| min-device-width | 设备的最小宽度。 |
| max-device-width | 设备的最大宽度。 |
| round-screen | 屏幕类型,圆形屏幕为true,非圆形屏幕为false。 |
| dark-mode | 系统为深色模式时为true,否则为false。 |
## 场景示例
下例中使用媒体查询,实现屏幕横竖屏切换时,给页面文本应用添加不同的内容和样式。
Stage模型下的示例:
```ts
import mediaquery from '@ohos.mediaquery';
import window from '@ohos.window';
import common from '@ohos.app.ability.common';
let portraitFunc = null;
@Entry
@Component
struct MediaQueryExample {
@State color: string = '#DB7093';
@State text: string = 'Portrait';
// 当设备横屏时条件成立
listener = mediaquery.matchMediaSync('(orientation: landscape)');
// 当满足媒体查询条件时,触发回调
onPortrait(mediaQueryResult) {
if (mediaQueryResult.matches) { // 若设备为横屏状态,更改相应的页面布局
this.color = '#FFD700';
this.text = 'Landscape';
} else {
this.color = '#DB7093';
this.text = 'Portrait';
}
}
aboutToAppear() {
// 绑定当前应用实例
portraitFunc = this.onPortrait.bind(this);
// 绑定回调函数
this.listener.on('change', portraitFunc);
}
// 改变设备横竖屏状态函数
private changeOrientation(isLandscape: boolean) {
// 获取UIAbility实例的上下文信息
let context = getContext(this) as common.UIAbilityContext;
// 调用该接口手动改变设备横竖屏状态
window.getLastWindow(context).then((lastWindow) => {
lastWindow.setPreferredOrientation(isLandscape ? window.Orientation.LANDSCAPE : window.Orientation.PORTRAIT)
});
}
build() {
Column({ space: 50 }) {
Text(this.text).fontSize(50).fontColor(this.color)
Text('Landscape').fontSize(50).fontColor(this.color).backgroundColor(Color.Orange)
.onClick(() => {
this.changeOrientation(true);
})
Text('Portrait').fontSize(50).fontColor(this.color).backgroundColor(Color.Orange)
.onClick(() => {
this.changeOrientation(false);
})
}
.width('100%').height('100%')
}
}
```
FA模型下的示例:
```ts
import mediaquery from '@ohos.mediaquery';
import featureAbility from '@ohos.ability.featureAbility';
let portraitFunc = null;
@Entry
@Component
struct MediaQueryExample {
@State color: string = '#DB7093';
@State text: string = 'Portrait';
listener = mediaquery.matchMediaSync('(orientation: landscape)'); // 当设备横屏时条件成立
onPortrait(mediaQueryResult) { // 当满足媒体查询条件时,触发回调
if (mediaQueryResult.matches) { // 若设备为横屏状态,更改相应的页面布局
this.color = '#FFD700';
this.text = 'Landscape';
} else {
this.color = '#DB7093';
this.text = 'Portrait';
}
}
aboutToAppear() {
portraitFunc = this.onPortrait.bind(this); // 绑定当前应用实例
this.listener.on('change', portraitFunc); //绑定回调函数
}
build() {
Column({ space: 50 }) {
Text(this.text).fontSize(50).fontColor(this.color)
Text('Landscape').fontSize(50).fontColor(this.color).backgroundColor(Color.Orange)
.onClick(() => {
let context = featureAbility.getContext();
context.setDisplayOrientation(0); //调用该接口手动改变设备横竖屏状态
})
Text('Portrait').fontSize(50).fontColor(this.color).backgroundColor(Color.Orange)
.onClick(() => {
let context = featureAbility.getContext();
context.setDisplayOrientation(1); //调用该接口手动改变设备横竖屏状态
})
}
.width('100%').height('100%')
}
}
```
**图1** 竖屏  
![portralit](figures/portralit.jpg)
**图2** 横屏  
![landscape](figures/landscape.jpg)
## 相关实例
基于媒体查询,可参考以下实例:
[媒体查询](https://gitee.com/openharmony/applications_app_samples/tree/master/ETSUI/MediaQuery):使用媒体查询,完成在不同设备上显示不同的界面效果。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册