赞
踩
添加控件的文章分成了上下两篇,上篇介绍了文本显示、文本输入、按钮、图片、单选框、切换按钮这六种常用控件,本篇继续介绍其他几种很重要但略微复杂的控件。
鸿蒙系列上一篇:
目录
进度条是我们日常开发中经常使用的一种控件,用于显示某种操作的进度,给用户以友好的提示,Progress是进度条显示组件。使用如下方法创建:
Progress(options: {value: number, total?: number, type?: ProgressType})
value为进度条的进度,total为进度的最大值,type为进度条的类型,支持如下几种类型:
ProgressType.Linear | 线型样式 |
ProgressType.Ring | 环形无刻度样式 |
ProgressType.ScaleRing | 环形有刻度样式 |
ProgressType.Eclipse | 圆形样式 |
ProgressType.Capsule | 胶囊样式 |
ProgressType.Linear创建线性样式进度条,线性样式的进度条默认水平展示,高度大于宽度的时候会自适应垂直显示:
- // 线性样式的进度条
- Progress({ value: 25, total: 100, type: ProgressType.Linear })
- .width('80%')
ProgressType.Ring创建环形无刻度样式进度条:
- // 环形无刻度进度条
- Progress({ value: 25, total: 100, type: ProgressType.Ring })
ProgressType.ScaleRing创建环形有刻度样式进度条,环形有刻度样式进度条需要设置style属性,否则跟环形无刻度进度条一样,看不出效果:
- // 环形有刻度进度条
- Progress({ value: 25, total: 100, type: ProgressType.ScaleRing })
- .style({ scaleCount: 20, scaleWidth: 3, strokeWidth: 10 }) // 20刻度,刻度宽度3,刻度长度10
ProgressType.Eclipse创建圆形样式进度条:
- // 圆形进度条
- Progress({ value: 25, total: 100, type: ProgressType.Eclipse })
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的使用与之前的控件有点区别。从名字也可以看出,既然是自定义弹窗,那么自然需要自定义一个控件,使用方式如下:
1、使用@CustomDialog装饰器装饰自定义弹窗
2、通过CustomDialogController类显示自定义弹窗
接下来,通过实现一个小demo来看下CustomDialog如何创建,并且实现对弹窗内部事件的监听和分发。demo效果如下:
在页面里面新建一个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%的屏幕宽度。
接下来,在页面中通过点击一个按钮去展现上面自定义的弹窗,并且分别监听其确认和取消按钮的点击事件。代码如下:
- @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里面写的,贴的上述代码我把上一节中进度条的代码给删掉了,但是最终的演示效果仍然是所有的代码,不要疑惑为什么我的页面上方多了那么多进度条。
视频的场景更为广泛,前几年短视频兴起后,抖音、快手等短视频app都用到了视频播放组件。Video组件的使用方式如下:
Video(value: {src?: string | Resource, currentProgressRate?: number | string | PlaybackSpeed, previewUri?: string | PixelMap | Resource, controller?: VideoController})
其中,src指定视频播放源的路径,currentProgressRate用于设置视频播放倍速,previewUri指定视频的封面图路径,controller设置视频控制器,用于自定义控制视频。
加载本地视频时,首先在本地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
- })
- }
- }
- }
支持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
- })
- }
- }
- }
日常开发中最常用的应该还是播放网络视频。加载网络视频时,需要申请权限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 })
来看下效果(吐槽一下:鸿蒙的模拟器真的太卡了,为了录个屏模拟器重启和清数据了好几次,唉.....就这还打造屁的生态):
气泡是一个比较特殊的控件,因为它需要作为属性去绑定到其他的组件上,在组件上显示气泡弹窗提示。气泡分为两种类型,一种是系统气泡,一种是自定义气泡。
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、增加了两个按钮,点击时控制气泡消失
看下效果:
可以使用构建器创建自定义气泡,@Builder中可以放自定义的内容。除此之外,还可以通过popupColor等参数控制气泡样式。
使用@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%')
- }
绑定自定义气泡到控件上,实现同样跟上面系统气泡一样的显隐逻辑,代码如下:
- @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: '气泡消失' })
- }
- }
- })
看下效果:
菜单需要调用bindMenu接口来实现,如下是使用系统默认菜单:
- // 默认菜单
- Text('测试菜单的文本框')
- .borderWidth(1)
- .padding(4)
- .margin({ top: 10 })
- .bindMenu([{ value: '菜单1'
- , action: () => {
- promptAction.showToast({ message: '菜单1' })
- } },
- { value: '菜单2'
- , action: () => {
- promptAction.showToast({ message: '菜单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)
- }
- }
- }
分别看下以上两种菜单的效果:
菜单默认是点击的方式弹出,比如我们上述使用bindMenu的方式。如果想长按触发呢?可以使用bindContextMenu设置菜单弹出的触发方式:右键或长按。我们把上述弹出自定义菜单的代码稍微修改下,如下:
- // 自定义菜单
- Text('测试自定义菜单的文本框')
- .borderWidth(1)
- .padding(4)
- .margin({ top: 10 })
- .bindContextMenu(this.MyMenu,ResponseType.LongPress)
至此,本篇就介绍到这里了。这篇文章断断续续写了好几天,相比起上一篇添加基础控件,本篇的控件确实复杂了一些。用了两章的篇幅,我们把鸿蒙的基础系统控件都学习完了。下一篇,我们不妨一起来一次UI实战,把近期掌握的布局和控件一起在实战中检验下。
最后,分享一款免费看热门电影和电视剧的app,仅限Android:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。