鸿蒙开发:支持自定义组件的跑马灯

前言

最近的项目中用到了,可以连续滚动的跑马灯,并且滚动的不仅仅是纯文字,还有图片等其他的组件,那么这种情况下,系统为我们提供的Text组件或者Marquee组件就无法来实现了,只能自己自定义了。

我们先看下自定义后的效果:

总体而言,自定义之后的跑马灯,无论是纵向滚动还是横向滚动,是连续滚动还是停顿滚动,基本上都实现了,可以说,满足了实际开发中的大多数场景,那么如何实现以上的效果呢?其实很简单,一个Swiper组件和一个属性动画就可以实现了。

那么在自定义实现之前,有两个问题询问大家,第一个问题是,系统的Text组件或者Marquee组件,支持纵向滚动吗?

第二个问题是,如果我想连续滚动图文结构的内容,或者自定义组件滚动,系统是否为我们提供了可用组件?

可以明确的是,以上两个问题的答案都是否定的,系统的Text组件或者Marquee组件,只支持文本滚动,并且也支持横向滚动,所以,如果你想实现非文本滚动,或者纵向滚动,只能自己动手来实现。

本篇文章的大致内容如下:

1、停顿跑马灯实现

2、连续跑马灯实现

3、marquee组件全实现

4、相关总结

停顿跑马灯实现

所谓的停顿,就是跑马灯数据,暂停一些时间,然后再去轮动下一个,周而复始的展示;这种方式实现起来最简单,相信聪明的你,一下就能想起来了如何实现,没错,就是使用Swiper组件。

系统的Swiper组件,一般用于轮播图的实现,但是可以发现,这个暂停的跑马灯,其实就是一个轮播图。

横向也好,纵向也好,通过vertical属性便可轻松搞定,为true就纵向,fasle就是横向;对于时间上的控制,可以使用interval属性来设置。

简单的案例代码如下:

TypeScript 复制代码
 Swiper() {
          ForEach(this.marqueeData, (item: Object, index: number) => {
            this.marqueeLayout(item, index)
          })
        }
        .vertical(this.marqueeDirection == MarqueeDirection.vertical ? true : false)
        .autoPlay(this.autoPlay) //是否是自动循环
        .loop(this.isLoop)
        .interval(this.interval) //间隔时间
        .indicator(false)
        .disableSwipe(true)

Swiper组件是支持任意的自定义组件的,所以,如果你的项目中有停顿跑马灯效果的话,完全可以进行使用。

连续跑马灯实现

连续的跑马灯,就是一直滚动,不存在中间停顿的时间,上一个滚动完成之后,接着就滚动下一个,纯文本的横向滚动,我们可以使用系统的Text组件或者Marquee组件来实现,如果滚动的是非文本,那么就需要自定义来实现了。

自定义起来也是非常的简单,只需要两个属性便可以搞定,一个是平移属性动画,一个是设置动画参数,如果是横向,那么就设置x坐标,如果是纵向,就设置y坐标。

TypeScript 复制代码
 .translate({ y: this.offsetY })
            .animation({
              duration: this.interval,
              curve: Curve.Linear, // 线性速度
              iterations: this.isLoop ? -1 : this.iterations, // 无限循环
              playMode: this.marqueeFromStart ? PlayMode.Normal : PlayMode.Reverse, // 单向播放
            })

无论是纵向移动还是横向移动,所移动的距离,一定是整个组件的距离,需要自己动态来计算,比如纵向滚动,单个组件高度为50,共有5个组件需要轮动,那就是5*5,也就是轮动的组件数量乘以单个组件的高度。

marquee组件全实现

无论连续还是停顿,文本还是非文本,目前这些功能我全部已经封装完毕,发布到了中心仓库中,大家可以直接依赖使用。

地址:ohpm.openharmony.cn/#/cn/detail...

快速使用

方式一:在Terminal窗口中,执行如下命令安装三方包,DevEco Studio会自动在工程的oh-package.json5中自动添加三方包依赖。

建议:在使用的模块路径下进行执行命令。

TypeScript 复制代码
ohpm install @abner/marquee

方式二:在工程模块的oh-package.json5中设置三方包依赖,配置示例如下:

TypeScript 复制代码
"dependencies": { "@abner/marquee": "^1.0.0"}

代码使用

1、纯文字跑马灯

TypeScript 复制代码
MarqueeView({
  marqueeText: "我是一段纯文本内容,主要用来展示跑马灯效果,来吧,我们一起看一下吧",
})

属性介绍

属性 类型 概述
marqueeText string/Resource 跑马灯文本数据
marqueeTextAttribute MarqueeTextAttribute 跑马灯文本属性,设置颜色大小等
marqueeStart boolean 控制跑马灯播放和停止,true播放,false暂停
marqueeTextStep number 滚动动画文本滚动步长。默认值:4.0vp
marqueeFromStart boolean 设置文本从头开始滚动或反向滚动。true表示从头开始滚动,false表示反向滚动。默认值:true
marqueeTextDelay number 设置每次滚动的时间间隔。默认值:0单位:毫秒
marqueeTextLoop number 设置重复滚动的次数,小于等于零时无限循环。默认值:-1
marqueeTextFadeout boolean 设置文字超长时的渐隐效果。true表示支持渐隐效果,false表示不支持渐隐效果。

MarqueeTextAttribute

属性 类型 概述
fontColor ResourceColor 跑马灯文本颜色
fontSize number / string/ Resource 跑马灯文本颜色
fontFamily string / Resource 字体族。默认字体'HarmonyOS Sans'。
fontStyle FontStyle 字体样式。默认值:FontStyle.Normal
fontWeight number / FontWeight / ResourceStr 文本的字体粗细

2、纵向停顿跑马灯

TypeScript 复制代码
@Entry
  @Component
  struct VerticalPausePage {
    private marqueeDataArray: string[] = [
      "公告:我是测试消息第一条",
      "公告:我是测试消息第二条",
      "公告:我是测试消息第三条"
    ]

    /**
   *AUTHOR:AbnerMing
   *INTRODUCE:可以是任意布局
   */
    @Builder
    marqueeLayout(item: string, _: number) {
      Row() {
        SymbolGlyph($r('sys.symbol.bell_fill'))
          .fontSize(18)
          .renderingStrategy(SymbolRenderingStrategy.SINGLE)
          .fontColor([Color.Red])
          .margin({ left: 5 })
        Text(item)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .margin({ left: 5 })
      }.width("100%")
        .height(50)
    }

    build() {
      RelativeContainer() {
        ActionBar({ title: "纵向停顿跑马灯" })

        Column() {
          MarqueeView({
            marqueeData: this.marqueeDataArray, //可以传递任意类型数据数组
            marqueeType: MarqueeType.pause,
            interval:1000,
            marqueeLayout: (item: Object, index: number) => {
              this.marqueeLayout(item as string, index)
            }
          })
        }.border({ width: 1, color: Color.Red })
          .margin({ left: 10, right: 10 })
          .alignRules({
            center: { anchor: '__container__', align: VerticalAlign.Center },
            middle: { anchor: '__container__', align: HorizontalAlign.Center }
          })
      }
      .height('100%')
        .width('100%')
    }
  }

属性介绍

属性 类型 概述
marqueeData Object[] 跑马灯数据数组
marqueeLayout @BuilderParam 用于传递跑马灯自定义组件,可以自定义@Builder来实现
isLoop boolean 是否是循环播放,默认true为循环
interval number 跑马灯间隔时间,默认是3000毫秒
autoPlay boolean 是否是自动播放,默认true,是
marqueeType MarqueeType 跑马灯类型,默认为文本类型,MarqueeType.text,pause:停顿类型,continuous:连续类型
iterations number 循环次数,默认无限次
marqueeStart boolean true播放,false暂停

3、纵向连续跑马灯

TypeScript 复制代码
@Entry
  @Component
  struct VerticalContinuousPage {
    private marqueeDataArray: string[] = [
      "公告:我是测试消息第一条",
      "公告:我是测试消息第二条",
      "公告:我是测试消息第三条",
      "公告:我是测试消息第四条",
      "公告:我是测试消息第五条"
    ]

    /**
   *AUTHOR:AbnerMing
   *INTRODUCE:可以是任意布局
   */
    @Builder
    marqueeLayout(item: string, _: number) {
      Row() {
        SymbolGlyph($r('sys.symbol.bell_fill'))
          .fontSize(18)
          .renderingStrategy(SymbolRenderingStrategy.SINGLE)
          .fontColor([Color.Red])
          .margin({ left: 5 })
        Text(item)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .margin({ left: 5 })
      }.width("100%")
        .height(50)
    }

    /**
   *AUTHOR:AbnerMing
   *INTRODUCE:可以是任意布局
   */
    @Builder
    marqueeText(item: string, _: number) {
      Text(item)
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .margin({ left: 5 })
        .width("100%")
        .height(50)

    }

    /**
   *AUTHOR:AbnerMing
   *INTRODUCE:可以是任意布局
   */
    @Builder
    marqueeLayout2(item: string, _: number) {
      Column() {
        Image("https://loveharmony.oss-cn-beijing.aliyuncs.com/banner/banner_01.jpg")
          .width("100%")
          .height(100)
        Text(item)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .margin({ left: 5, top: 10 })
      }.width("100%")
        .padding(10)
    }

    build() {
      Column() {
        ActionBar({ title: "纵向连续跑马灯" })

        //纯文本
        MarqueeView({
          marqueeData: this.marqueeDataArray, //可以传递任意类型数据数组
          marqueeType: MarqueeType.continuous, //连续滚动
          marqueeLayout: (item: Object, index: number) => {
            this.marqueeText(item as string, index)
          }
        }).border({ width: 1, color: Color.Red })

        //自定义布局1
        MarqueeView({
          marqueeData: this.marqueeDataArray, //可以传递任意类型数据数组
          marqueeType: MarqueeType.continuous, //连续滚动
          marqueeLayout: (item: Object, index: number) => {
            this.marqueeLayout(item as string, index)
          }
        }).border({ width: 1, color: Color.Red })
          .margin({ top: 20 })

        //自定义布局2
        MarqueeView({
          marqueeData: this.marqueeDataArray, //可以传递任意类型数据数组
          marqueeType: MarqueeType.continuous, //连续滚动
          marqueeLayout: (item: Object, index: number) => {
            this.marqueeLayout2(item as string, index)
          }
        }).border({ width: 1, color: Color.Red })
          .margin({ top: 20 })

      }.padding({ left: 20, right: 20 })
        .height('100%')
        .width('100%')
    }
  }

4、横向停顿跑马灯

TypeScript 复制代码
@Entry
  @Component
  struct HorizontalPausePage {
    private marqueeDataArray: string[] = [
      "公告:我是测试消息第一条",
      "公告:我是测试消息第二条",
      "公告:我是测试消息第三条"
    ]

    /**
   *AUTHOR:AbnerMing
   *INTRODUCE:可以是任意布局
   */
    @Builder
    marqueeLayout(item: string, index: number) {
      Row() {
        SymbolGlyph($r('sys.symbol.bell_fill'))
          .fontSize(18)
          .renderingStrategy(SymbolRenderingStrategy.SINGLE)
          .fontColor([Color.Red])
          .margin({ left: 5 })
        Text(item)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .margin({ left: 5 })
      }.width("100%")
        .height(50)
        .justifyContent(FlexAlign.Center) //内容居中
    }

    build() {
      Column() {
        ActionBar({ title: "横向停顿跑马灯" })
        Column() {
          MarqueeView({
            marqueeData: this.marqueeDataArray, //可以传递任意类型数据数组
            marqueeDirection: MarqueeDirection.horizontal, //横向滚动
            marqueeType: MarqueeType.pause,
            marqueeLayout: (item: Object, index: number) => {
              this.marqueeLayout(item as string, index)
            }
          })
        }.border({ width: 1, color: Color.Red })
          .margin({ left: 10, right: 10, top: 50 })
      }
      .height('100%')
        .width('100%')
    }
  }

5、横向连续跑马灯

TypeScript 复制代码
@Entry
  @Component
  struct HorizontalContinuousPage {
    private marqueeDataArray: string[] = [
      "公告:我是测试消息第一条",
      "公告:我是测试消息第二条",
      "公告:我是测试消息第三条"
    ]

    /**
   *AUTHOR:AbnerMing
   *INTRODUCE:可以是任意布局
   */
    @Builder
    marqueeText(item: string, _: number) {
      Text(item)
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .margin({ left: 5 })
        .width("100%")
        .height(50)

    }

    /**
   *AUTHOR:AbnerMing
   *INTRODUCE:可以是任意布局
   */
    @Builder
    marqueeLayout(item: string, _: number) {
      Row() {
        SymbolGlyph($r('sys.symbol.bell_fill'))
          .fontSize(18)
          .renderingStrategy(SymbolRenderingStrategy.SINGLE)
          .fontColor([Color.Red])
          .margin({ left: 5 })
        Text(item)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .margin({ left: 5 })
      }.width("100%")
        .height(50)
    }


    /**
   *AUTHOR:AbnerMing
   *INTRODUCE:可以是任意布局
   */
    @Builder
    marqueeLayout2(item: string, _: number) {
      Column() {
        Image("https://loveharmony.oss-cn-beijing.aliyuncs.com/banner/banner_01.jpg")
          .width("100%")
          .height(100)
        Text(item)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .margin({ left: 5, top: 10 })
      }.width("100%")
        .padding(10)
    }

    build() {
      Column() {
        ActionBar({ title: "横向连续跑马灯" })

        //纯文本
        MarqueeView({
          marqueeData: this.marqueeDataArray, //可以传递任意类型数据数组
          marqueeType: MarqueeType.continuous, //连续滚动
          marqueeDirection: MarqueeDirection.horizontal, //横向
          marqueeLayout: (item: Object, index: number) => {
            this.marqueeText(item as string, index)
          }
        }).border({ width: 1, color: Color.Red })

        //自定义布局1
        MarqueeView({
          marqueeData: this.marqueeDataArray, //可以传递任意类型数据数组
          marqueeType: MarqueeType.continuous, //连续滚动
          marqueeDirection: MarqueeDirection.horizontal, //横向
          marqueeLayout: (item: Object, index: number) => {
            this.marqueeLayout(item as string, index)
          }
        }).border({ width: 1, color: Color.Red })
          .margin({ top: 20 })

        //自定义布局2
        MarqueeView({
          marqueeData: this.marqueeDataArray, //可以传递任意类型数据数组
          marqueeType: MarqueeType.continuous, //连续滚动
          marqueeDirection: MarqueeDirection.horizontal, //横向
          marqueeLayout: (item: Object, index: number) => {
            this.marqueeLayout2(item as string, index)
          }
        }).border({ width: 1, color: Color.Red })
          .margin({ top: 20 })

      }
      .height('100%')
        .width('100%')
        .padding({ left: 10, right: 10 })
    }
  }

相关总结

在实际的开发中,如果是横向的连续滚动,并且是文本类型的,那么建议直接使用系统的Text组件或者Marquee组件,当然了,也可以使用,我开发好的marquee组件,里面也实现了系统的能力,其实是一样的。如果是非文本,那没有别的办法,只能使用自定义的marquee组件。

相关推荐
用户41659673693552 小时前
优化 WebView 图片长按体验:JS Bridge 实现原生与网页端分发机制
android
Jeled3 小时前
RecyclerView ViewHolder 复用机制详解(含常见错乱问题与优化方案)
android·学习·面试·kotlin
2501_915106323 小时前
iOS 抓包全流程指南,HTTPS 抓包、TCP 数据流分析与多工具协同的方法论
android·tcp/ip·ios·小程序·https·uni-app·iphone
2013编程爱好者3 小时前
【HUAWEI】HUAWEI Mate 70 Air详解
华为·harmonyos
程序员 _孜然5 小时前
【最详细】android-studio-2025.2.x新版本,导出apk,含jks证书生成
android·ide·android studio
珹洺5 小时前
Java-Spring实战指南(三十四)Android Service实现后台音乐播放功能
android·java·spring
柯南二号5 小时前
【大前端】【Android】 Kotlin 语法超详细解析(2025 最新)
android·kotlin
爱笑的眼睛117 小时前
HarmonyOS USB设备管理深度探索:从基础到高级应用
华为·harmonyos
爱笑的眼睛1110 小时前
HarmonyOS文件压缩与解压缩API深度解析与实践
华为·harmonyos