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

前言

最近的项目中用到了,可以连续滚动的跑马灯,并且滚动的不仅仅是纯文字,还有图片等其他的组件,那么这种情况下,系统为我们提供的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组件。

相关推荐
无风之翼1 小时前
android15 休眠唤醒过程中有时候屏幕显示时间一闪而过
android·锁屏
如此风景3 小时前
iOS SwiftUI开发所有修饰符使用详解
ios
mumuWorld3 小时前
KSCrash 实现机制深度分析
ios·源码阅读
方白羽3 小时前
Android全局悬浮拖拽视图
android·app·客户端
AskHarries4 小时前
Google 登录问题排查指南
flutter·ios·app
waeng_luo4 小时前
[鸿蒙2025领航者闯关] HarmonyOS深色模式实现
harmonyos·鸿蒙2025领航者闯关·鸿蒙6实战·开发者年度总结
灯前目力虽非昔,犹课蝇头二万言。4 小时前
HarmonyOS笔记9:UIAbility之间的切换和数据的传递
笔记·harmonyos
Jerry4 小时前
Compose 高级状态和附带效应
android
花启莫你是不是傻4 小时前
在鸿蒙中调用 FFmpeg 命令行工具
华为·harmonyos
崽崽长肉肉5 小时前
Swift中的知识点总结
ios·swift