鸿蒙开发(八)添加常用控件(下)

    添加控件的文章分成了上下两篇,上篇介绍了文本显示、文本输入、按钮、图片、单选框、切换按钮这六种常用控件,本篇继续介绍其他几种很重要但略微复杂的控件。

鸿蒙系列上一篇:

鸿蒙开发(七)添加常用控件(上)-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实战,把近期掌握的布局和控件一起在实战中检验下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/458654.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】串 解析+完整代码(求子串、比大小、定位操作)

1.串的实现 1.1 串的定义 定义 串,即字符串,是由零个或多个字符组成的有限序列。 串是一种特殊的线性表,数据元素间呈线性关系。 空串:串长度为0时;子串:串中任意个连续的字符组成的子序列;主串…

ConcurrentHashMap 为什么不能插入 null?

1、典型回答 简单来说,ConcurrentHashMap 不允许插入 null 值是JDK 源码规定的,如下源码所示(此源码基于JDK 1.8): 从上述源码可以看出,在添加方法的第一句就加了判断:如果 key 值为 null 或者是 value 值为 null&…

Spring Cloud Alibaba微服务从入门到进阶(一)(SpringBoot三板斧、SpringBoot Actuator)

Springboot三板斧 1、加依赖 2、写注解 3、写配置 Spring Boot Actuator Spring Boot Actuator 是 Spring Boot 提供的一系列用于监控和管理应用程序的工具和服务。 SpringBoot导航端点 其中localhost:8080/actuator/health是健康检查端点,加上以下配置&#xf…

基于PHP构建的HTML5点餐系统的设计13.91

随着互联网时代的发展,人们的生活方式正在发生改变。传统的餐饮行业也正在发生变革。人们不再满足过去的点餐方式,需要更好的体验。本课题旨在结合点餐系统的技术优势,设计一个能够方便顾客与商家,并且节约人力成本以及可以很好地…

中国金融统计年鉴、中国保险统计年鉴、中国人口与就业统计年鉴、国民经济和社会发展公报、中国劳动统计年鉴

数据下载链接:百度云下载链接 统计年鉴是指以统计图表和分析说明为主,通过高度密集的统计数据来全面、系统、连续地记录年度经济、社会等各方面发展情况的大型工具书来获取统计数据资料。 统计年鉴是进行各项经济、社会研究的必要前提。而借助于统计年…

Java代码基础算法练习---2024.3.14

其实这就是从我学校的资源,都比较基础的算法题,先尽量每天都做1-2题,练手感。毕竟离我真正去尝试入职好的公司(我指的就是中大厂,但是任重道远啊),仍有一定的时间,至少要等我升本之后…

【黑马程序员】Python文件、异常、模块、包

文章目录 文件操作文件编码什么是编码为什么要使用编码 文件的读取openmodel常用的三种基础访问模式读操作相关方法 文件的写入注意代码示例 异常定义异常捕获捕获指定异常捕获多个异常捕获所有异常异常else异常finally 异常的传递 python 模块定义模块的导入import模块名from …

【北京大学】徐高《金融经济学二十五讲》

一、经济的任务 经济的任务之一是确保有效地分配稀缺资源,这是经济学中的一个核心问题。资源是有限的,而需求是无限的,因此经济系统需要通过合理的机制来分配资源以满足社会的需求。以下是关于经济分配资源的几个方面: 1. 资源配…

【RPG Maker MV 仿新仙剑 战斗场景UI (三)】

RPG Maker MV 仿新仙剑 战斗场景UI 三 二级战斗指令菜单RMMV效果代码效果 仿仙剑UI代码效果 二级战斗指令菜单 仙剑1中二级战斗的菜单内容如下:物品、防御、围攻、逃跑、状态这五项。 现在来完成金玉其外的UI部分,内核具体的功能需要后期进行填充了&…

聚酰亚胺PI材料难于粘接,用什么胶水粘接?那么让我们先一步步的从认识它开始(一)

聚酰亚胺PI的基本概念 聚酰亚胺(Polyimide,简称PI)是一种重要的高性能聚合物材料。是指主链上含有酰亚胺环的一类聚合物,是综合性能最佳的有机高分子材料之一。它具有最高的阻燃等级(UL-94),以及…

C语言从入门到实战————数组和指针的深入理解

前言 在C语言中,数组和指针有的密切得联系,因为数组名本身就相当于一个指针常量。指针是一个变量,专门用来存储另一个变量的内存地址,通过这个地址可以访问和操作该变量的值,同时也包括数组。数组是一组连续存储的同类…

离线安装数据库 mysql 5.7 linux

离线安装数据库 mysql 5.7 linux 方法一 参考链接Linux(Debian10.2)安装MySQL5.7.24环境 赋予文件执行权限chmod x 文件名 使用root用户sudo su解压文件tar xvf mysql-5.7.42-linux-glibc2.12-x86_64.tar.gz重命名mv mysql-5.7.42-linux-glibc2.12-x86_64 mysql将桌面的mys…

【WSL】Windows wsl2 子系统忘记密码,重置修改用户密码

1.问题 windows 子系统 ubuntu 忘记密码,sudo 命令无法使用,需要重置密码 2. 解决 使用 wsl 命令进行修改,打开 cmd 窗口 # root 打开 wsl --user root # 修改 root 密码 passwd root # 修改用户密码 passwd username

【ARM】DS中Coretex-M处理器的常用寄存器介绍

【更多软件使用问题请点击亿道电子官方网站查询】 1、 文档目标 了解ArmDS中Coretex-M处理器的常用寄存器的名称及作用。 2、 问题场景 在对Coretex-M处理器进行开发时,了解常用寄存器的名称及作用,可以: 编写正确的程序: 寄存器是程序员用…

AI会取代低代码吗?——探讨两者在软件开发中的角色和关系

引言 在当今快速发展的数字化时代,软件开发已成为企业和商户必不可少的一项工作。为了应对不断增长的需求和日益复杂的业务要求,开发人员和企业正在寻求更加高效、快速的软件开发解决方案。在这样的背景下,低代码开发平台和人工智能&#xf…

【嵌入式开发·Arduino板】I2C接口通讯及应用 | 串口通讯实例 | I2C的类库函数,I2C接口的应用

“跟猫学,保持冷漠,适当撒娇,几乎不动心。跟猪学,保持食欲,充足睡眠,几乎不烦恼。” 🎯作者主页: 追光者♂🔥 🌸个人简介: 💖[1] 计算机专业硕士研究生💖 🌿[2] 2023年城市之星领跑者TOP1(哈尔滨)🌿 🌟[3] 2022年度博客之星人工智能领域…

集合系列(四) -LinkedHashMap详解

一、摘要 在集合系列的第一章,咱们了解到,Map的实现类有HashMap、LinkedHashMap、TreeMap、IdentityHashMap、WeakHashMap、Hashtable、Properties等等。 本文主要从数据结构和算法层面,探讨LinkedHashMap的实现。 二、简介 LinkedHashMap可…

虚拟机网络链接

在虚拟网络设置中找到如下界面: "子网 IP" 192.168.79.0/24 表示一个局域网络,它有254个可能的IP地址可供分配(192.168.79.1到192.168.79.254),255.255.255.0 是子网掩码,定义了网络和主机部分。…

python练习一

1. 五个PPT上的界面打印【print、input函数】 print("\t\t\t\t\t英雄联盟商城登录界面\n~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~\n\t\t\t\t\t1. 用户登录\n\t\t\t\t\t2. 新用户注册\n\t\t\t\t\t3. 退出系统\n" "~ * ~ * ~ * ~ * ~ * ~ * ~…

代码随想录算法训练营第二十三天 | 77. 组合

回溯 77. 组合 题目链接&#xff1a;https://leetcode.cn/problems/combinations/ 文章讲解&#xff1a;https://programmercarl.com/0077.%E7%BB%84%E5%90%88.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV1ti4y1L7cv/ class Solution { private:vector<…