一、引言

轮播图是移动应用中最高频的 UI 模式之一——打开电商 App 首页,顶部横幅轮播正滚动着促销活动;天气 App 中,城市卡片左右滑动切换;社交 App 的开屏引导页,一页页解释着新功能。轮播组件不仅承载着核心内容的展示,更是应用的"第一印象",直接影响用户的操作直觉和浏览体验。

传统开发中,实现轮播需要组合 ScrollView + PageIndicator + Timer 定时器 + 手势处理等多套机制。这些组件之间需要精确协调——定时器启动/停止的时机、手势与自动播放的互斥、最后一页滑动后是否回到第一页——任一环节的疏忽都会导致体验 Bug(如双指滑动触发自动翻页、快速滑动导致页面错位)。

而 HarmonyOS 提供了 Swiper 组件——一个专用于轮播的容器组件。它在一套 API 中集成了内容滑动(水平/垂直)、自动播放(autoPlay)、无限循环(loop)、指示器(圆点/数字/无)、动画控制(duration/curve),以及 SwiperController 手动控制切换。开发者只需声明 Swiper 的属性,将子页面放入容器,即可获得一个功能完备的轮播组件。

本文通过一个"轮播图展廊"Demo 深入讲解 Swiper 组件的核心用法:如何设置自动播放和切换间隔?圆点指示器与数字指示器如何切换?无限循环如何开启?以及如何通过 SwiperController 实现手动导航和缩略图导航。

阅读完本文,你将能够:

  • 使用 Swiper 组件构建轮播图
  • 配置 autoPlay、loop、interval 等自动播放属性
  • 使用 Indicator.dot() 和 Indicator.digit() 定制指示器样式
  • 使用 SwiperController 编程控制翻页
  • 构建完整的轮播图展廊应用

二、Swiper 组件 API 总览

2.1 构造函数

Swiper(controller?: SwiperController) {
  // 子组件——每个直接子组件为一个轮播页
}
参数 类型 说明
controller SwiperController 控制器,用于编程控制翻页(prev/next/jumpTo)

Swiper 是一个容器组件,其每个直接子组件代表一个轮播页面。子组件通常使用 ForEach 从数据数组动态生成。如果传入 SwiperController 实例,即可在代码中调用 showNext()showPrevious()changeIndex(idx) 等方法控制翻页。

2.2 链式方法

// 是否自动播放
.autoPlay(value: boolean): SwiperAttribute

// 是否无限循环(最后一页滑到第一页,第一页滑到最后一页)
.loop(value: boolean): SwiperAttribute

// 自动播放间隔(毫秒),仅 autoPlay=true 时生效
.interval(value: number): SwiperAttribute

// 滑动动画时长(毫秒)
.duration(value: number): SwiperAttribute

// 是否垂直滑动(默认 false 即水平滑动)
.vertical(value: boolean): SwiperAttribute

// 动画曲线
.curve(value: Curve | string): SwiperAttribute

// 指示器样式
.indicator(value: DotIndicator | DigitIndicator | boolean): SwiperAttribute

// 翻页变化回调(返回当前页索引)
.onChange(callback: (index: number) => void): SwiperAttribute

// 动画开始回调
.onAnimationStart(callback: (index: number) => void): SwiperAttribute
方法 说明
.autoPlay(boolean) 是否自动轮播,false 时仅响应手势滑动
.loop(boolean) 是否无限循环。true 时首尾相连;false 时到达边界无法继续滑动
.interval(number) 自动播放间隔,单位毫秒。默认 3000ms。仅 autoPlay=true 时有效
.duration(number) 切换动画的时长,单位毫秒。建议 200~600ms
.vertical(boolean) true 即垂直方向滑动(如抖音信息流),默认 false(水平滑动)
.curve(Curve) 动画缓动曲线,默认 Curve.Linear。可设 Curve.Ease / Curve.FastOutSlowIn 等
.indicator(...) 设置指示器。支持 DotIndicator(圆点)、DigitIndicator(数字比例)、false(隐藏)
.onChange(Callback<number>) 页码变化回调,返回当前页索引。用于绑定外部状态(如缩略图高亮)
.onAnimationStart(Callback<number>) 切换动画开始时触发,可用于动画开始前的前置逻辑

2.3 Indicator 类型

Swiper 内置了两种指示器样式:

// 圆点指示器
Indicator.dot()
  .itemWidth(8)          // 圆点宽度(vp)
  .itemHeight(8)         // 圆点高度(vp)
  .selectedItemWidth(16) // 选中圆点宽度
  .selectedItemHeight(8) // 选中圆点高度
  .color('#CCCCCC')      // 未选中颜色
  .selectedColor('#1677FF') // 选中颜色
  .left(0)               // 距左侧距离
  .right(12)             // 距右侧距离
  .top(0)                // 距顶部距离
  .bottom(12)            // 距底部距离

// 数字指示器
Indicator.digit()
  .fontColor('#FFFFFF')  // 文字颜色
  .selectedFontColor('#FFFFFF') // 选中文字颜色
  .digitFont({ size: 12 })     // 字体
  .selectedDigitFont({ size: 14 })
  .right(12)
  .bottom(12)
指示器类型 视觉效果 适用场景
Indicator.dot() 一排小圆点,选中项高亮放大 内容型轮播(banner、引导页、图片浏览)
Indicator.digit() “1 / 5” 格式的数字比例 图片浏览器、相册详情
false 无指示器 自定义指示器、仅手势滑动的场景

2.4 SwiperController 方法

interface SwiperController {
  showNext(): void;        // 切换到下一页
  showPrevious(): void;    // 切换到上一页
  changeIndex(value: number): void; // 切换到指定索引页
  finishAnimation(): void; // 停止当前动画
}

SwiperController 是 Swiper 组件与外部逻辑之间的桥梁。Demo 中的上一页/下一页按钮、缩略图导航均通过控制器实现。注意:changeIndex(index) 会触发过渡动画,而直接修改 Swiper 内部状态不经过控制器则可能跳过动画。

2.5 Swiper 与 Tabs 的区别

特性 Swiper Tabs
核心用途 内容轮播(广告、引导页、图片浏览) 页面切换(Tab 导航)
切换方式 手势滑动 + 自动播放 + 编程控制 点击 Tab 标签 + 手势滑动
指示器 dot / digit 内置样式 TabBar 自定义标签
循环 loop 属性支持 不直接支持循环
自动播放 autoPlay + interval 支持 不支持

简言之:需要自动轮播、循环切换的场景(banner、引导页、图片浏览)用 Swiper;需要 Tab 导航切换的场景(首页/消息/我的)用 Tabs。
在这里插入图片描述

三、Demo 设计:轮播图展廊

3.1 功能概述

Demo 是一个"轮播图展廊"应用,模拟内容型应用中的 banner 横幅轮播功能:

  1. 主轮播区:5 张彩色内容卡片(220vp 高度),每张展示标题、副标题和页码,支持手势滑动切换
  2. 自动播放:默认开启,每 3 秒自动翻页,可随时切换开关状态
  3. 无限循环:最后一页右滑回到第一页,第一页左滑滑到最后一页
  4. 指示器切换:圆点 → 数字 → 隐藏,三种指示器样式实时切换
  5. 切换间隔:2 秒 / 3 秒 / 5 秒三档可选
  6. 手动导航:上一页 / 下一页按钮,列表点击跳转
  7. 缩略导航条:主轮播下方彩色进度条,点击直接跳转
  8. 页面列表:所有轮播页面清单,当前页高亮显示

3.2 交互点

# 交互 说明
1 手势滑动轮播 左右滑动切换轮播页面,onChange 更新当前索引
2 自动播放开关 Switch 切换自动播放,开启时按 interval 定时翻页
3 指示器切换 切换圆点/数字/隐藏三种指示器样式,实时反映在轮播上
4 手动导航 上一页/下一页按钮 + 缩略条点击 + 列表点击,均通过 SwiperController 控制

四、完整代码实现

在这里插入图片描述

4.1 数据模型与状态

interface SlideData {
  title: string;
  subtitle: string;
  bgColor: string;
  accentColor: string;
}

@State currentIndex: number = 0;
@State autoPlayOn: boolean = true;
@State useDigit: boolean = false;
@State showIndicator: boolean = true;
@State loopOn: boolean = true;
@State intervalMs: number = 3000;

private swiperCtrl: SwiperController = new SwiperController();

private slides: SlideData[] = [
  { title: '鸿蒙新特性', subtitle: '探索 HarmonyOS NEXT 最新 API 和组件', bgColor: '#1677FF', accentColor: '#FFFFFF' },
  { title: '原生开发', subtitle: '纯鸿蒙原生应用,极致性能体验', bgColor: '#52C41A', accentColor: '#FFFFFF' },
  { title: '跨平台方案', subtitle: '一套代码,多端统一运行', bgColor: '#FF9800', accentColor: '#FFFFFF' },
  { title: '高性能渲染', subtitle: 'ArkUI 声明式框架,流畅 60fps', bgColor: '#9C27B0', accentColor: '#FFFFFF' },
  { title: '安全可信', subtitle: '系统级安全防护,数据全程加密', bgColor: '#FF4D4F', accentColor: '#FFFFFF' }
];

每条轮播数据包含标题、副标题、背景色和装饰色。SwiperController 实例在组件初始化时创建,传递给 Swiper 组件后即可在回调方法中调用翻页控制。五个 @State 变量分别控制自动播放状态、指示器类型、循环模式和切换间隔。

4.2 主轮播区

Swiper(this.swiperCtrl) {
  ForEach(this.slides, (slide: SlideData, idx: number) => {
    Column() {
      Text(slide.title)
        .fontSize(28)
        .fontColor(slide.accentColor)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 10 })

      Text(slide.subtitle)
        .fontSize(14)
        .fontColor(slide.accentColor.concat('CC'))  // 副标题半透明

      Text((idx + 1).toString().concat(' / ', this.slides.length.toString()))
        .fontSize(11)
        .fontColor(slide.accentColor.concat('88'))  // 页码更透明
        .margin({ top: 20 })
    }
    .width('100%')
    .height(220)
    .justifyContent(FlexAlign.Center)
    .backgroundColor(slide.bgColor)
  }, (slide: SlideData, idx: number) => slide.title)
}
.autoPlay(this.autoPlayOn)
.loop(this.loopOn)
.interval(this.intervalMs)
.duration(400)
.indicator(this.showIndicator ?
  (this.useDigit ?
    Indicator.digit().right(12).bottom(12) :
    Indicator.dot().bottom(12)) :
  false)
.onChange((index: number) => { this.currentIndex = index; })

逐行解析:

  • ForEach 生成页面:5 张卡片,每张填满 100% 宽度和 220vp 高度,内容垂直居中
  • .autoPlay(this.autoPlayOn):绑定自动播放开关。autoPlayOn 为 true 时定时翻页,false 时仅响应手势
  • .loop(this.loopOn):无限循环开关。开启后首尾相连,最后一页继续滑动回到第一页
  • .interval(this.intervalMs):自动播放间隔,默认 3000ms。仅当 autoPlay 为 true 时生效
  • .duration(400):翻页动画 400ms。值越小切换越快,值越大过渡越平滑
  • .indicator(...):动态选择指示器——this.useDigit 为 true 时显示数字指示器(“1/5”),否则显示圆点。this.showIndicator 为 false 时隐藏(传入 false)
  • .onChange(...):页码变化时更新 currentIndex,驱动缩略导航条和页面列表的高亮状态

4.3 缩略导航条

Row() {
  ForEach(this.slides, (slide: SlideData, idx: number) => {
    Row()
      .width(40)
      .height(4)
      .borderRadius(2)
      .backgroundColor(idx === this.currentIndex ? slide.bgColor : '#E8E8E8')
      .margin({ left: 4, right: 4 })
      .onClick(() => { this.swiperCtrl.changeIndex(idx); })
  }, (slide: SlideData, idx: number) => slide.title)
}
.width('100%')
.justifyContent(FlexAlign.Center)
.margin({ top: 14 })

缩略导航条位于主轮播下方,由一排 40×4vp 的圆角条组成。当前页对应的条显示为该页背景色,其余为浅灰色。点击任意条调用 this.swiperCtrl.changeIndex(idx) 跳转到对应页面——这是"编程控制翻页"的典型用法。

4.4 播放控制区

// 自动播放开关
Row() {
  Text('自动播放')
    .fontSize(14).fontColor('#1a1a2e').fontWeight(FontWeight.Medium)
    .layoutWeight(1)
  Toggle({ type: ToggleType.Switch, isOn: this.autoPlayOn })
    .selectedColor('#1677FF')
    .onChange((value: boolean) => { this.autoPlayOn = value; })
}

// 无限循环
Row() {
  Text('无限循环')
    .fontSize(14).fontColor('#1a1a2e').fontWeight(FontWeight.Medium)
    .layoutWeight(1)
  Toggle({ type: ToggleType.Switch, isOn: this.loopOn })
    .selectedColor('#1677FF')
    .onChange((value: boolean) => { this.loopOn = value; })
}

两个 Switch 开关分别控制自动播放和无限循环。关闭自动播放后轮播不会自动翻页但仍可手势滑动;关闭无限循环后到达边界时无法继续滑动(第一页无法滑到最后一页)。

指示器类型三选一按钮(圆点/数字/隐藏)和切换间隔三选一按钮(2s/3s/5s)使用统一的预设值选择器模式——选中项蓝色背景白色文字,未选中项灰色背景。

4.5 手动导航按钮

Row() {
  Text('◀ 上一页')
    .fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Medium)
    .padding({ top: 10, bottom: 10, left: 20, right: 20 })
    .borderRadius(10).backgroundColor('#1677FF')
    .onClick(() => { this.swiperCtrl.showPrevious(); })

  Blank()

  Text('下一页 ▶')
    .fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Medium)
    .padding({ top: 10, bottom: 10, left: 20, right: 20 })
    .borderRadius(10).backgroundColor('#1677FF')
    .onClick(() => { this.swiperCtrl.showNext(); })
}
.width('100%')

两个按钮分别调用 showPrevious()showNext(),提供了手势滑动之外的另一种切换方式。这对于以下场景尤为重要:老年用户不习惯手势滑动;展示型大屏(平板/车机)不方便手势操作;自动化演示场景需要程序控制翻页。

4.6 页面列表

ForEach(this.slides, (slide: SlideData, idx: number) => {
  Row() {
    Column() {
      Text(slide.title)
        .fontSize(15)
        .fontColor(this.currentIndex === idx ? slide.bgColor : '#1a1a2e')
        .fontWeight(this.currentIndex === idx ? FontWeight.Bold : FontWeight.Medium)
      Text(slide.subtitle)
        .fontSize(11).fontColor('#9999AA')
        .margin({ top: 2 })
    }
    .alignItems(HorizontalAlign.Start)
    .layoutWeight(1)

    if (this.currentIndex === idx) {
      Row()
        .width(8).height(8).borderRadius(4)
        .backgroundColor(slide.bgColor)
    }
  }
  .width('100%')
  .padding({ top: 12, bottom: 12, left: 4, right: 4 })
  .border({ width: { bottom: 0.5 }, color: '#F2F3F5' })
  .onClick(() => { this.swiperCtrl.changeIndex(idx); })
}, (slide: SlideData, idx: number) => slide.title)

页面列表展示所有 5 张轮播卡片的标题和副标题。当前页标题显示为该页背景色并加粗,右侧显示一个 8×8vp 的实心圆点作为标记。点击任意行跳转到对应页面。这是第三种导航方式——与缩略导航条互补,帮助用户在冗长的轮播列表中快速定位目标页面。

五、关键技术点详解

5.1 autoPlay 与 interval 的协作机制

autoPlay 是自动播放的总开关,interval 是两次切换之间的等待时间(毫秒)。两者的协作逻辑:

  • autoPlay = false:无论 interval 设为多少,都不会自动翻页。用户只能通过手势滑动或编程控制翻页
  • autoPlay = true:每隔 interval 毫秒自动调用一次翻页。翻页方向由当前滑动方向决定(水平模式下默认向右翻)

默认 interval 为 3000ms。实际选择 interval 时应考虑:内容信息量(信息量大需要更长的阅读时间 → 5000ms+)、页面数量(页面越多,用户浏览一轮的时间越长)、业务需求(促销 banner 需要快速曝光多个活动 → 2000ms)。

Demo 中提供 2s/3s/5s 三档可选,让用户直观感受不同间隔的轮播节奏差异。

5.2 loop 与边界行为

.loop(true) 打开无限循环后,Swiper 内部会做两件事:

  1. 首尾相连:在第一页继续向左滑动(调用 showPrevious())会直接跳到最后一页,反之亦然
  2. 页面复用:循环模式下 Swiper 不会创建无限个页面实例——它通过内部的页面缓存池复用页面节点,保证内存使用可控

.loop(false) 关闭循环后:

  1. 在第一页时"上一页"按钮(showPrevious())无效
  2. 在最后一页时自动播放停止(如果有 autoPlay
  3. 手势滑动到边界时出现回弹效果

Demo 中的"无限循环"开关绑定了 this.loopOn,用户关闭后可以立即体验边界限制的区别——尝试在第一页按"上一页"按钮,Swip er 将无响应。

5.3 指示器的定位与定制

Indicator.dot()Indicator.digit() 都提供 .left().right().top().bottom() 四个定位属性,用于在轮播容器内放置指示器。默认值:

  • .dot() 默认 .bottom(12) 水平居中(居中逻辑由 Swiper 内部处理)
  • .digit() 默认 .right(12).bottom(12) 右下角

指示器定位使用相对于 Swiper 容器边界的偏移距离,不受轮播页面内容影响。这意味着即使轮播页背景色不同,指示器的位置始终固定。

圆点指示器的视觉定制包括尺寸(.itemWidth/.itemHeight)、间距(.itemSpace)、颜色(.color/.selectedColor)和选中项尺寸(.selectedItemWidth/.selectedItemHeight)。Demo 中使用默认样式以保持简洁,实际项目中可根据设计规范进行定制。

5.4 SwiperController 的使用时机

SwiperController 提供了三种方法:showNext()showPrevious()changeIndex(idx)。这些方法可以在任何事件回调中调用(如 onClick),但需要注意:

  1. 控制器必须在组件构建前创建new SwiperController() 放在 @Stateprivate 变量初始化阶段,不能放在 build() 方法内——否则每次重建都会创建新实例,导致控制器引用失效
  2. 循环模式下的索引处理:在 loop = true 时调用 changeIndex(0) 总会跳转到第一页的实际内容(而不是循环后的某个副本)
  3. 动画会排队执行:快速连续调用 showNext() 时,Swiper 会将每次翻页请求排队,依次执行动画——不会出现页面跳过或动画混乱

Demo 中三种导航方式(手动按钮、缩略条、列表点击)都通过控制器实现,展示了"相同控制器,多种触发方式"的设计模式。

5.5 性能考量:大量页面的处理

对于轮播页面数量很多(如 20+ 张商品图片)的场景,每个页面作为一个独立的子组件,渲染过多页面会影响首屏加载速度。优化策略:

  1. 懒加载模式:Swiper 默认只创建当前页和相邻页的组件节点(内部缓存池),并非一次性创建所有页面。这个行为类似于 Flutter 的 PageView 和 Android 的 ViewPager2

  2. 使用图片懒加载:如果每页包含大图,结合 Image.syncLoad(false)(异步加载默认值)让图片在页面即将显示时才加载,而非一次性加载所有图片

  3. 控制 duration 时长.duration() 设置过短(< 100ms)会造成视觉闪烁,设置过长(> 1000ms)会导致用户等待不耐烦。推荐 300~500ms 的过渡时长,Demo 中使用 400ms

  4. 避免在 onChange 中执行繁重操作onChange 在每次翻页时触发,如果回调中执行大量计算或网络请求,会导致滑动卡顿。应保持 onChange 内的逻辑轻量(如仅更新状态变量)

5.6 方向模式:水平 vs 垂直

.vertical(boolean) 控制滑动方向。false(默认)为水平滑动,适用于横幅轮播、引导页、图片浏览器。true 为垂直滑动,适用于全屏信息流、短视频、卡片式浏览。

垂直模式下,指示器的 .left()/.right() 定位属性对应的物理边不变(仍相对于屏幕的左右),但 .top()/.bottom() 对应垂直方向。如果指示器放在侧边(如 .right(12).vertical(true)),圆点会呈纵向排列。

Demo 中未使用垂直模式以保持 Demo 的聚焦性,但在实际项目(如短视频类应用)中,垂直 Swiper 是核心交互组件。

六、运行效果

6.1 初始状态

进入"轮播图展廊"页面,顶部深色标题栏显示"轮播图展廊",下方白色信息卡片介绍 Swiper 组件。主轮播区(220vp 高度)展示第 1 张蓝色卡片"鸿蒙新特性 — 探索 HarmonyOS NEXT 最新 API 和组件",底部显示圆点指示器(5 个圆点,第一个高亮)。缩略导航条第一个条为蓝色,其余为灰色。

三秒后自动切换到绿色"原生开发"页面,圆点指示器第 2 个高亮,缩略条第 2 个变绿。AutoPlay 开关默认开启状态(绿色 Switch)。

6.2 手势滑动

手指向左滑动轮播区 → 页面切换到下一页,onChange 更新 currentIndex,指示器和缩略条同步变化。向右滑动切换到上一页。手势滑动时自动播放的计时器会重置(从当前页重新计算 interval)。

6.3 切换指示器

点击"数字"按钮 → 圆点指示器消失,右下角出现数字指示器"2 / 5"(当前在第 2 页时)。再次点击"隐藏"→ 指示器完全消失,轮播区仅显示页面内容。点击"圆点"→ 恢复圆点指示器。三种指示器切换流畅,轮播本身不闪烁不重建。

6.4 手动导航

点击"上一页"按钮 → 轮播切换到上一张卡片,动画 400ms。快速连续点击"下一页"按钮多次 → 每次翻页动画依次执行,不跳页不混乱。点击缩略导航条第 4 个灰色条 → 轮播直接跳转到"高性能渲染"页面。

6.5 关闭自动播放与循环

关闭"自动播放"开关 → 轮播停止自动翻页,但手势滑动和按钮导航仍然有效。关闭"无限循环"开关 → 在"安全可信"(最后一页)点击"下一页"无反应。重新开启循环 → 最后一页点击"下一页"即可跳回"鸿蒙新特性"(第一页)。

6.6 切换间隔

将切换间隔从 3s 改为 2s → 轮播加速至每 2 秒翻页一次,节奏加快。改为 5s → 每页停留时间增长,适合信息量更大的卡片内容。切换间隔变更时自动播放的计时器立即重置。

七、总结

本文通过一个"轮播图展廊"实战 Demo,深入讲解了 HarmonyOS Swiper 轮播组件的核心用法:

  1. 基础轮播:Swiper 容器 + ForEach 生成子页面 + 手势滑动切换
  2. 自动播放.autoPlay(true) + .interval(ms) 实现定时翻页,Switch 开关控制启停
  3. 无限循环.loop(true) 实现首尾相连,关闭后边界回弹
  4. 指示器Indicator.dot()(圆点,适合内容轮播)和 Indicator.digit()(数字,适合图片浏览),通过 .indicator(false) 隐藏
  5. 编程控制SwiperController.showNext() / .showPrevious() / .changeIndex(idx) 实现手动导航、缩略条跳转和列表定位
  6. 动画定制.duration(ms) 控制切换动画快慢,.curve() 调整缓动曲线

Swiper 将"内容滑动 + 自动播放 + 指示器 + 循环 + 编程控制"整合进一套声明式 API,开发者只需关注轮播页面的内容配置参数,而不需要处理定时器生命周期、手势冲突、页面缓存等底层细节。希望本文能帮助你在实际项目中高效运用 Swiper 组件。


本文基于 HarmonyOS NEXT API 24 编写,代码经 DevEco Studio 6.1.1 编译验证通过。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐