添加控件的文章分成了上下两篇,上篇介绍了文本显示、文本输入、按钮、图片、单选框、切换按钮这六种常用控件,本篇继续介绍其他几种很重要但略微复杂的控件。
鸿蒙系列上一篇:
鸿蒙开发(七)添加常用控件(上)-CSDN博客文章浏览阅读673次,点赞19次,收藏15次。相信大家已经对鸿蒙开发的布局有了基本的了解。之前我们提到过,一个好的UI,离不开选择合理的布局。当然,也离不开适当的控件。本篇文章,带着大家一起学习下如何在页面里面添加常用的控件。由于控件较多,我会分为两篇文章来介绍。https://blog.csdn.net/qq_21154101/article/details/135747069?spm=1001.2014.3001.5501
目录
一、进度条(Progress)
1、线性样式
2、环形无刻度样式
3、环形有刻度样式
4、圆形样式
5、胶囊进度条
二、自定义弹窗(CustomDialog)
1、创建自定义弹窗
2、展现弹窗和监听事件
三、视频(Video)
1、播放本地视频
2、播放沙箱视频
3、播放网络视频
四、气泡(Popup)
1、系统气泡
2、自定义气泡
(1)创建自定义气泡
(2)绑定自定义气泡
五、菜单(Menu)
1、默认菜单
2、自定义菜单
3、支持长按的菜单
一、进度条(Progress)
进度条是我们日常开发中经常使用的一种控件,用于显示某种操作的进度,给用户以友好的提示,Progress是进度条显示组件。使用如下方法创建:
Progress(options: {value: number, total?: number, type?: ProgressType})
value为进度条的进度,total为进度的最大值,type为进度条的类型,支持如下几种类型:
ProgressType.Linear | 线型样式 |
ProgressType.Ring | 环形无刻度样式 |
ProgressType.ScaleRing | 环形有刻度样式 |
ProgressType.Eclipse | 圆形样式 |
ProgressType.Capsule | 胶囊样式 |
1、线性样式
ProgressType.Linear创建线性样式进度条,线性样式的进度条默认水平展示,高度大于宽度的时候会自适应垂直显示:
// 线性样式的进度条
Progress({ value: 25, total: 100, type: ProgressType.Linear })
.width('80%')
2、环形无刻度样式
ProgressType.Ring创建环形无刻度样式进度条:
// 环形无刻度进度条
Progress({ value: 25, total: 100, type: ProgressType.Ring })
3、环形有刻度样式
ProgressType.ScaleRing创建环形有刻度样式进度条,环形有刻度样式进度条需要设置style属性,否则跟环形无刻度进度条一样,看不出效果:
// 环形有刻度进度条
Progress({ value: 25, total: 100, type: ProgressType.ScaleRing })
.style({ scaleCount: 20, scaleWidth: 3, strokeWidth: 10 }) // 20刻度,刻度宽度3,刻度长度10
4、圆形样式
ProgressType.Eclipse创建圆形样式进度条:
// 圆形进度条
Progress({ value: 25, total: 100, type: ProgressType.Eclipse })
5、胶囊进度条
ProgressType.Capsule创建圆形样式进度条,需要设置宽高否则跟圆形样式一样,且宽>高时水平展示,高>宽时垂直展示:
// 胶囊进度条
Progress({ value: 25, total: 100, type: ProgressType.Capsule })
.width(50)
.height(100)
实现一个demo,使用一个Button控制进度条的进度,如下完整代码:
@Entry
@Component
struct AddWidget2 {
@State progress: number = 5
build() {
Row() {
Column() {
// 线性样式的进度条
Progress({ value: this.progress, total: 100, type: ProgressType.Linear })
.width('80%')
Row() {
// 环形无刻度进度条
Progress({ value: this.progress, total: 100, type: ProgressType.Ring })
// 环形有刻度进度条
Progress({ value: this.progress, total: 100, type: ProgressType.ScaleRing })
.style({ scaleCount: 20, scaleWidth: 3, strokeWidth: 10 }) // 20刻度,刻度宽度3,刻度长度10
.backgroundColor(Color.Gray) // 背景颜色灰色
.color(Color.Black) // 进度颜色黑色
// 圆形进度条
Progress({ value: this.progress, total: 100, type: ProgressType.Eclipse })
// 胶囊进度条
Progress({ value: this.progress, total: 100, type: ProgressType.Capsule })
.width(50)
.height(100)
}.width('100%').justifyContent(FlexAlign.SpaceEvenly)
Button('进度+5')
.onClick(() => {
this.progress += 5
if (this.progress > 100) {
this.progress = 0
}
})
}
.width('100%')
.margin({ top: 20 })
}
}
}
如下是上述代码的效果:
二、自定义弹窗(CustomDialog)
自定义弹窗(CustomDialog)主要用来给用户提示或警告用的。比如删除文件的二次确认,比如即将跳转某个页面的提示等,也可以用于广告、抽奖、签到等场景。
CustomDialog的使用与之前的控件有点区别。从名字也可以看出,既然是自定义弹窗,那么自然需要自定义一个控件,使用方式如下:
1、使用@CustomDialog装饰器装饰自定义弹窗
2、通过CustomDialogController类显示自定义弹窗
接下来,通过实现一个小demo来看下CustomDialog如何创建,并且实现对弹窗内部事件的监听和分发。demo效果如下:
1、创建自定义弹窗
在页面里面新建一个struct - MyDialog,创建自定义弹窗需要使用@CustomDialog注解修饰struct,如下所示:
@CustomDialog
struct MyDialog {
controller: CustomDialogController
cancel: () => void
confirm: () => void
build() {
Column() {
Text('确定要删除文件吗')
.fontSize(20)
.margin({ top: 20, bottom: 40 })
Row() {
Button('确认')
.onClick(() => {
this.controller.close()
this.confirm()
})
Button('取消')
.onClick(() => {
this.controller.close()
this.cancel()
})
}.width('75%').justifyContent(FlexAlign.SpaceAround).margin({ bottom: 20 })
}
}
}
简单解释下如上代码:
1、CustomDialogController用来控制弹窗的展现和消失。
2、两个接口cancel和confirm,分别用来监听和分发"确认"和"取消"两个按钮的点击事件。
3、布局内容也很简单,上面一个Text,下面两个Button,整个窗体占据75%的屏幕宽度。
2、展现弹窗和监听事件
接下来,在页面中通过点击一个按钮去展现上面自定义的弹窗,并且分别监听其确认和取消按钮的点击事件。代码如下:
@Entry
@Component
struct AddWidget2 {
@State progress: number = 5
dialogController: CustomDialogController = new CustomDialogController({
builder: MyDialog({
cancel: this.onCancel,
confirm: this.onConfirm
}),
alignment: DialogAlignment.Default
})
onCancel() {
promptAction.showToast({ message: '点击了取消' })
}
onConfirm() {
promptAction.showToast({ message: '点击了确认' })
}
build() {
Row() {
Column() {
Button('弹窗')
.onClick(() => {
this.dialogController.open()
}).margin({ top: 10 })
}
.width('100%')
.margin({ top: 20 })
}
}
最后,看下效果:
注:本篇的代码都在同一个页面AddWidget2里面写的,贴的上述代码我把上一节中进度条的代码给删掉了,但是最终的演示效果仍然是所有的代码,不要疑惑为什么我的页面上方多了那么多进度条。
三、视频(Video)
视频的场景更为广泛,前几年短视频兴起后,抖音、快手等短视频app都用到了视频播放组件。Video组件的使用方式如下:
Video(value: {src?: string | Resource, currentProgressRate?: number | string | PlaybackSpeed, previewUri?: string | PixelMap | Resource, controller?: VideoController})
其中,src指定视频播放源的路径,currentProgressRate用于设置视频播放倍速,previewUri指定视频的封面图路径,controller设置视频控制器,用于自定义控制视频。
1、播放本地视频
加载本地视频时,首先在本地rawfile目录指定对应的文件,如下图所示:
再使用资源访问符$rawfile()引用视频资源。
@Component
export struct VideoPlayer{
private controller:VideoController;
private previewUris: Resource = $r ('app.media.preview');
private innerResource: Resource = $rawfile('test.mp4');
build(){
Column() {
Video({
src: this.innerResource,
previewUri: this.previewUris,
controller: this.controller
})
}
}
}
2、播放沙箱视频
支持file:///data/storage路径前缀的字符串,用于读取应用沙箱路径内的资源。需要保证应用沙箱目录路径下的文件存在并且有可读权限。
@Component
export struct VideoPlayer {
private controller: VideoController;
private videoSrc: string = 'file:///data/storage/el2/base/haps/entry/files/show.mp4'
build() {
Column() {
Video({
src: this.videoSrc,
controller: this.controller
})
}
}
}
3、播放网络视频
日常开发中最常用的应该还是播放网络视频。加载网络视频时,需要申请权限ohos.permission.INTERNET:
@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
})
}
}
}
通过加载以上三种路径的视频可以看出,调用的都是同样的方法,只不过videoSrc不同。接下来,通过如下代码加载一个本地视频:
Video({
previewUri: ($r('app.media.icon')),
src: ($rawfile('test.mp4')),
controller: this.controller
})
.autoPlay(true)
.muted(true)
.loop(true)
.onStart(() => {
promptAction.showToast({ message: '开始播放' })
})
.onPause(() => {
promptAction.showToast({ message: '暂停播放' })
})
.onFinish(() => {
promptAction.showToast({ message: '结束播放' })
})
.width('90%')
.height('25%')
.margin({ top: 20 })
来看下效果(吐槽一下:鸿蒙的模拟器真的太卡了,为了录个屏模拟器重启和清数据了好几次,唉.....就这还打造屁的生态):
四、气泡(Popup)
气泡是一个比较特殊的控件,因为它需要作为属性去绑定到其他的组件上,在组件上显示气泡弹窗提示。气泡分为两种类型,一种是系统气泡,一种是自定义气泡。
1、系统气泡
PopupOptions为系统提供的气泡,可以通过配置primaryButton、secondaryButton来设置带按钮的气泡,跟用户做简单的交互。如下:
@State bindPop: boolean = false
// 气泡
Text('测试气泡用的文本框')
.onClick(() => {
this.bindPop = !this.bindPop
})
.borderWidth(1)
.padding(4)
.margin({ top: 10 })
.bindPopup(this.bindPop, { message: '这里是气泡',
onStateChange: (event) => {
if (event.isVisible) {
promptAction.showToast({ message: '气泡可见' })
} else {
promptAction.showToast({ message: '气泡消失' })
}
},
primaryButton: {
value: '确定',
action: () => {
this.bindPop = false
}
},
secondaryButton: {
value: '取消',
action: () => {
this.bindPop = false
}
}})
1、定义了一个@State变量bindPop,用来通过其变化即时刷新气泡显示与隐藏
2、增加了状态监听onStateChange,并且弹toast告诉我们是显示还是隐藏
3、增加了两个按钮,点击时控制气泡消失
看下效果:
2、自定义气泡
可以使用构建器创建自定义气泡,@Builder中可以放自定义的内容。除此之外,还可以通过popupColor等参数控制气泡样式。
(1)创建自定义气泡
使用@Builder注解创建自定义气泡,并且实现自定义的布局和控件,如下代码:
@Builder
popupBuilder() {
Column() {
Row() {
Image($r("app.media.icon"))
.width(24)
.height(24)
.margin({ left: 20 })
Text('这是自定义气泡')
.fontSize(15)
.margin({ left: 20 })
}.margin({ top: 20 })
Column() {
Button('按钮1')
.margin({ top: 10 })
.onClick(() => {
promptAction.showToast({ message: '点击了按钮1' })
})
Button('按钮2')
.margin({ top: 10 })
.onClick(() => {
promptAction.showToast({ message: '点击了按钮2' })
})
Button('按钮3')
.margin({ top: 10 })
.onClick(() => {
promptAction.showToast({ message: '点击了按钮3' })
})
}.margin({ bottom: 20 })
}.width('80%')
}
(2)绑定自定义气泡
绑定自定义气泡到控件上,实现同样跟上面系统气泡一样的显隐逻辑,代码如下:
@State bindCustomPop: boolean = false
// 自定义气泡
Text('测试自定义气泡用的文本框')
.onClick(() => {
this.bindCustomPop = !this.bindCustomPop
})
.borderWidth(1)
.padding(4)
.margin({ top: 10 })
.bindPopup(this.bindCustomPop, {
builder: this.popupBuilder, // 气泡的内容
placement: Placement.Bottom, // 气泡的弹出位置
popupColor: Color.Gray, // 气泡的背景色
onStateChange: (event) => {
if (event.isVisible) {
promptAction.showToast({ message: '气泡可见' })
} else {
promptAction.showToast({ message: '气泡消失' })
}
}
})
看下效果:
五、菜单(Menu)
1、默认菜单
菜单需要调用bindMenu接口来实现,如下是使用系统默认菜单:
// 默认菜单
Text('测试菜单的文本框')
.borderWidth(1)
.padding(4)
.margin({ top: 10 })
.bindMenu([{ value: '菜单1'
, action: () => {
promptAction.showToast({ message: '菜单1' })
} },
{ value: '菜单2'
, action: () => {
promptAction.showToast({ message: '菜单2' })
} }])
2、自定义菜单
如果系统默认的菜单样式满足不了我们的需求,可以使用@Builder自定义菜单,然后使用BindMenu(自定义Menu)的方式绑定,如下:
// 自定义菜单
Text('测试自定义菜单的文本框')
.borderWidth(1)
.padding(4)
.margin({ top: 10 })
.bindMenu(this.MyMenu())
}
/**
* 自定义菜单
*/
@Builder
MyMenu() {
Menu() {
// 菜单选项1
MenuItem({
startIcon: $r("app.media.icon"),
content: "菜单选项1" })
.onChange(() => {
promptAction.showToast({ message: "点了菜单选项1" })
})
// 包含两个菜单的菜单组
MenuItemGroup({ header: '小标题' }) {
MenuItem({ content: "菜单选项2" })
.onChange((selected) => {
promptAction.showToast({ message: "点了菜单选项2" })
})
MenuItem({ startIcon: $r("app.media.icon"), content: "菜单选项3" }).enabled(false)
}
}
}
分别看下以上两种菜单的效果:
3、支持长按的菜单
菜单默认是点击的方式弹出,比如我们上述使用bindMenu的方式。如果想长按触发呢?可以使用bindContextMenu设置菜单弹出的触发方式:右键或长按。我们把上述弹出自定义菜单的代码稍微修改下,如下:
// 自定义菜单
Text('测试自定义菜单的文本框')
.borderWidth(1)
.padding(4)
.margin({ top: 10 })
.bindContextMenu(this.MyMenu,ResponseType.LongPress)
至此,本篇就介绍到这里了。这篇文章断断续续写了好几天,相比起上一篇添加基础控件,本篇的控件确实复杂了一些。用了两章的篇幅,我们把鸿蒙的基础系统控件都学习完了。下一篇,我们不妨一起来一次UI实战,把近期掌握的布局和控件一起在实战中检验下。