一、引言

轮播图(Carousel)是移动应用中最常见的视觉组件之一。从电商首页的促销 Banner、社交应用的图片浏览、到旅游 App 的目的地推荐——Swiper 以有限的空间承载了无限的内容浏览能力。用户通过左右滑动手势在页面之间自由切换,配合指示器(indicator)感知当前位置和总页数,配合自动播放(autoPlay)实现"不操作也能看"的被动浏览体验。

ArkUI 提供了原生的 Swiper 组件,将轮播这种复杂的交互模式封装为一个声明式的容器组件。开发者只需将子组件放入 Swiper 内部,每个子组件自动成为一个轮播页,Swiper 接管所有滑动手势、过渡动画、边界处理和指示器渲染。通过 SwiperController,你可以用代码控制"跳转到第 N 页"、“下一页”、“上一页”;通过 autoPlayinterval,你可以配置自动播放的行为;通过 loop,你可以让轮播无限循环;通过 vertical,你可以将水平轮播切换为纵向轮播。

更重要的是,Swiper 在 HarmonyOS NEXT 中经历了多次迭代增强——从基础的 index + indicator,到 AutoPlayOptions(触摸时暂停等精细控制)、customContentTransition(自定义切换动画)、displayCount(多页同显)和 prevMargin/nextMargin(露出前后页边缘)。这些增强让 Swiper 不仅是一个"轮播器",更是一个通用的"滑动页面容器"。

本文将通过一个完整的**“旅行目的地轮播”**实战案例,深入解析 Swiper 组件的构造方式、指示器定制、自动播放控制、方向切换以及缩略图导航联动。阅读完本文,你将能够:

  • 掌握 Swiper 组件的核心 API(index、autoPlay、interval、loop、vertical)
  • 使用 SwiperController 实现代码级别的页面跳转
  • 理解 autoPlay、interval 和 loop 的协同工作机制
  • 构建缩略图导航与主轮播的双向联动
  • 实现方向切换(横向/纵向)和循环模式的动态控制

二、Swiper 核心 API 详解

2.1 构造函数与控制器

Swiper 的构造函数只接受一个可选参数:

Swiper(controller?: SwiperController)

SwiperController 是控制轮播页切换的命令式接口:

declare class SwiperController {
  changeIndex(value: number, useAnimation?: boolean): void;  // 跳转到指定页
  showNext(): void;                                            // 下一页
  showPrevious(): void;                                        // 上一页
  finishAnimation(callback?: VoidCallback): void;              // 完成当前动画
}

在我们的 Demo 中,SwiperController 主要用于缩略图导航——用户点击某个缩略图时,调用 changeIndex 跳转到对应的轮播页:

private swiperController: SwiperController = new SwiperController();

// 在缩略图的 onClick 中:
this.swiperController.changeIndex(idx, false);

第二个参数 useAnimation: false 表示不使用过渡动画,立即跳转到目标页。这在缩略图导航中是恰当的选择——用户期望的是"瞬间切换"而非"滑动过渡"。如果需要平滑的滑动效果,传入 true 或省略此参数即可。

2.2 index:当前位置的桥梁

index 属性承担两个角色:

  1. 读取:当前显示的页面索引(从 0 开始)
  2. 写入:设置索引来跳转到指定页面
.index(this.currentIndex)       // 设置初始索引
.onChange((index: number) => {  // 当用户滑动切换时,同步更新索引
  this.currentIndex = index;
})

.onChange 回调在每次页面切换完成时触发,参数 index 是新页面的位置。通过将 this.currentIndex 与 Swiper 的 index 绑定,我们建立了一个双向同步的关系:Swiper 的页面变化会更新 @State currentIndex,而 currentIndex 的变化(比如通过缩略图点击触发)也会通过 .index(this.currentIndex) 控制 Swiper 的当前页。

这种双向绑定是 Swiper 与其他 UI 元素联动的核心机制。在我们的页面中,currentIndex 同时在以下几个地方被使用:

  • Swiper 的 .index() 属性
  • 缩略图的选中状态高亮(边框 + 背景色)
  • 页面指示器(由 Swiper 自动管理,通过 .indicator(true) 启用)

2.3 autoPlay 与 interval:自动轮播的协同机制

自动播放由两个独立的 API 控制:

.autoPlay(value: boolean)                       // 是否开启自动播放
.autoPlay(value: boolean, options: AutoPlayOptions)  // 带配置的自动播放(API 18+)
.interval(value: number)                        // 自动播放间隔(毫秒)

关键点autoPlay 控制"是否自动播",interval 控制"多久播一次"。两者是正交的——你可以设置 autoPlay(true) 但使用不同的 interval 值来改变轮播速度。

AutoPlayOptions(API 18+)提供的精细控制:

interface AutoPlayOptions {
  stopWhenTouched: boolean;  // 用户触摸时是否暂停自动播放,默认 true
}

stopWhenTouched = true(默认行为)意味着:当用户手指触碰 Swiper 时,自动播放立刻停止计时;手指离开后,自动播放从当前页重新开始计时。这是一个很细节但重要的 UX 设计——如果用户正在浏览某一页的内容(手指按住),轮播不应该把页面"拽走"。

在我们的 Demo 中,autoPlay 受一个开关按钮控制,interval 通过 4 个选项按钮(2s / 3s / 5s / 8s)选择。当用户切换 interval 时,新的间隔值立即通过 .interval(this.getSelectedInterval()) 生效:

getSelectedInterval(): number {
  return INTERVALS[this.intervalIndex].value;
}

在这里插入图片描述

2.4 loop:无限循环播放

.loop(value: boolean)

loop(true) 让轮播在达到最后一页后自动跳回第一页(或从第一页向前滑动到最后一页),实现"无限循环"的效果。loop(false) 则禁止循环,到达边界时滑动操作被阻止。

在 Demo 中,loop 通过一个带 SymbolGlyph 图标的按钮切换,选中状态为绿色(#52C41A),未选中为灰色。这是一个典型的"偏好设置"级 UI——用户通常不需要频繁切换循环模式,但知道可以切换。

2.5 vertical:方向切换

.vertical(value: boolean)

vertical(false)(默认)为横向轮播——页面从左向右排列,滑动手势为左右方向。vertical(true) 为纵向轮播——页面从上到下排列,滑动手势为上下方向。

方向切换是一个有趣的交互设计选择。大多数轮播图使用横向模式(符合人类阅读照片/图册的直觉),但纵向模式在某些场景下更自然——比如"上下翻页的文章阅读器"或"垂直滚动的短视频 Feed"。在我们的 Demo 中,方向切换按钮允许用户在两种模式之间快速切换,直观地比较它们的不同。

2.6 indicator:页面指示器

.indicator(value: boolean | DotIndicator | DigitIndicator)
  • false:不显示指示器
  • true:显示默认的圆点指示器
  • DotIndicator:自定义样式的圆点指示器(API 10+)
  • DigitIndicator:数字指示器,显示"2/5"格式

在我们的 Demo 中,使用了最简单的 .indicator(true)——在深色背景的轮播卡上,默认的白色圆点指示器清晰可见且不抢眼。对于更复杂的指示器需求(比如自定义位置、颜色、形状),可以通过 DotIndicator 或自定义 IndicatorComponentController 实现。

2.7 duration:切换动画时长

.duration(value: number)  // 毫秒

控制页面切换动画的持续时间。默认值约为 400ms。较短的 duration(如 200ms)让切换感觉"干脆利落",较长的 duration(如 800ms)让切换感觉"优雅平滑"。在我们的 Demo 中使用了 400ms,这是最常见的折中选择。

三、实战:旅行目的地轮播

3.1 页面整体设计

旅行目的地轮播围绕"浏览旅游城市"的场景设计,包含以下功能模块:

  1. 主轮播区(260vp 高度)— 5 个城市卡片,每张有独特的底色(红/紫/蓝/金/绿)+ emoji + 城市名/国家/描述
  2. 缩略图导航行 — 5 个小图标按钮,点击跳转到对应轮播页
  3. 自动播放开关 — 开启/关闭两个按钮互斥选择
  4. 轮播间隔选择 — 2s / 3s / 5s / 8s 四个选项
  5. 播放模式 — 循环开关 + 横向/纵向切换

整体的设计语言是"沉浸式浏览"——主轮播使用大面积的纯色背景 + emoji 插画,营造了一种类似"旅游宣传海报"的视觉氛围。每个城市的 color 属性既是背景色也是缩略图的选中色和主题色,保证了视觉一致性。

3.2 数据模型

interface SlideItem {
  city: string;
  country: string;
  image: string;
  desc: string;
  color: string;
}

const SLIDES: SlideItem[] = [
  { city: '东京', country: '日本', image: '🗼', desc: '传统与现代交融的魅力都市', color: '#FF6B6B' },
  { city: '巴黎', country: '法国', image: '🗼', desc: '浪漫与艺术的世界之都', color: '#722ED1' },
  { city: '纽约', country: '美国', image: '🗽', desc: '永不沉睡的摩天大楼之城', color: '#1677FF' },
  { city: '罗马', country: '意大利', image: '🏛️', desc: '千年文明的永恒之城', color: '#FAAD14' },
  { city: '悉尼', country: '澳大利亚', image: '🦘', desc: '阳光沙滩与歌剧院的港湾', color: '#52C41A' },
];

每个城市的 color 字段经过精心选择,确保 5 个颜色在视觉上相互区分且都足够鲜明——粉色(东京)、紫色(巴黎)、蓝色(纽约)、金色(罗马)、绿色(悉尼)。这 5 种颜色覆盖了可见光谱的暖色(粉/金)和冷色(蓝/绿/紫),在轮播滑动时产生自然的色调变化,增强了"翻阅不同城市"的视觉感受。

3.3 缩略图导航:SwiperController 的双向绑定

缩略图行位于主轮播区正下方,5 个缩略图并排显示:

Row() {
  ForEach(SLIDES, (slide: SlideItem, idx: number) => {
    Column() {
      Text(slide.image).fontSize(22)
    }
    .width(52)
    .height(52)
    .borderRadius(BorderRadius.MD)
    .backgroundColor(this.currentIndex === idx ? slide.color : '#F0F0F5')
    .border({ width: this.currentIndex === idx ? 2 : 0, color: slide.color })
    .onClick(() => {
      this.swiperController.changeIndex(idx, false);
    })
  })
}

缩略图通过 this.currentIndex === idx 来判断选中状态——选中的缩略图使用该城市的主色调作为背景色并有 2vp 边框,未选中的使用浅灰色背景。

点击缩略图时调用 this.swiperController.changeIndex(idx, false)——立即跳转到目标页。反过来,当用户手动滑动 Swiper 时,onChange 回调更新 this.currentIndex,缩略图状态自动同步。

这个双向联动的模式是 Swiper 交互设计中的经典范例——主视图(Swiper)和辅助视图(缩略图)通过共享一个 @State currentIndex 实现同步,无需手动协调。

3.4 自动播放的开关控制

自动播放使用两个互斥按钮实现——“开启"和"关闭”:

Row() {
  Row() {
    Text('开启')
      .backgroundColor(this.autoPlay ? AppColors.PRIMARY : '#F0F0F5')
      .fontColor(this.autoPlay ? '#FFFFFF' : AppColors.TEXT_SECONDARY)
      .onClick(() => { this.autoPlay = true; })
  }

  Row() {
    Text('关闭')
      .backgroundColor(!this.autoPlay ? '#FF4D4F' : '#F0F0F5')
      .fontColor(!this.autoPlay ? '#FFFFFF' : AppColors.TEXT_SECONDARY)
      .onClick(() => { this.autoPlay = false; })
  }
}

“开启"按钮在 active 时使用主题蓝色(AppColors.PRIMARY),“关闭"按钮在 active 时使用红色(#FF4D4F)——红色暗示"正在关闭一个功能”,而非简单的"未选中"状态。这种颜色语义在 UI 设计中很重要:当用户看到红色按钮高亮时,会自然地意识到"自动播放已被禁用”。

3.5 interval 的动态调整

轮播间隔通过 4 个选项按钮选择:

const INTERVALS: IntervalOption[] = [
  { label: '2s', value: 2000 },
  { label: '3s', value: 3000 },
  { label: '5s', value: 5000 },
  { label: '8s', value: 8000 },
];

value 字段存储的是毫秒值(interval() API 以毫秒为单位),但 label 显示为秒——用户更容易理解"5 秒"而非"5000 毫秒"。这是一个简单的数据转换:在数据层存储程序需要的格式(毫秒),在展示层显示用户需要的格式(秒)。

当用户切换间隔时,this.intervalIndex 更新,getSelectedInterval() 返回新的毫秒值,.interval() 属性更新——Swiper 的自动播放间隔立即改变。不需要重启自动播放或重建组件。

3.6 循环与方向的组合

循环开关和方向切换被组合在"播放模式"面板中:

// 循环播放开关
Row() {
  SymbolGlyph($r('sys.symbol.arrow_clockwise'))
    .fontSize(18)
    .fontColor([this.isLooping ? '#52C41A' : '#C9CDD4'])
  Text(this.isLooping ? '循环播放' : '不循环')
    .fontColor(this.isLooping ? '#52C41A' : AppColors.TEXT_TERTIARY)
}
.onClick(() => { this.isLooping = !this.isLooping; })

// 方向切换
Row() {
  SymbolGlyph($r('sys.symbol.heart'))
    .fontSize(18)
    .fontColor([this.isVertical ? '#722ED1' : '#C9CDD4'])
  Text(this.isVertical ? '纵向' : '横向')
    .fontColor(this.isVertical ? '#722ED1' : AppColors.TEXT_TERTIARY)
}
.onClick(() => { this.isVertical = !this.isVertical; })

两个选项都使用了 SymbolGlyph 图标作为视觉线索——arrow_clockwise 代表"循环/重复"(语义贴切),heart 代表"方向偏好"(使用 SymbolGlyph 渲染而非纯文本保证图标质量)。每个选项在 active 时都用一个专属的颜色标记——绿色代表"循环已开启"(正向行为),紫色代表"纵向模式"(特殊模式)。

点击方向切换按钮时,this.isVertical 取反,触发 .vertical(this.isVertical) 的更新——Swiper 从横向切换到纵向(或反之),当前页面保持不变。这个切换是瞬时的:用户点击按钮后,Swiper 周围的文字和内容会立刻按新的方向重新排列。

3.7 Swiper 的内部结构

每个轮播页面的内容:

Swiper(this.swiperController) {
  ForEach(SLIDES, (slide: SlideItem, idx: number) => {
    Column() {
      Text(slide.image).fontSize(72)      // 大 emoji 插画
      Text(slide.city).fontSize(24)       // 城市名
      Text(slide.country).fontSize(14)   // 国家
      Text(slide.desc).fontSize(12)      // 描述
    }
    .width('100%')
    .height(260)
    .backgroundColor(slide.color)
    .justifyContent(FlexAlign.Center)
  })
}

每个轮播页都是一个宽 100%、高 260vp 的 Column,背景色为该城市的主题色。内容使用 emoji 作为"插画"、城市名作为标题、国家名作为副标题、描述作为正文——构成了一个简洁但有层次的信息卡片。

emoji 的 fontSize(72) 选择了较大的尺寸——在 260vp 高的卡片上,72vp 的 emoji 约占 28% 的空间,这正是视觉中心的最佳比例。更大的 emoji 会产生压迫感,更小的则不够突出。文字部分使用三种不同的字号和透明度(#FFFFFF#FFFFFFCC#FFFFFF99),在白色文字上建立清晰的层级:城市名 > 国家名 > 描述。
在这里插入图片描述

四、完整代码结构

页面组件树:

Column
├── Row(标题栏:"旅行目的地")
└── Scroll
    └── Column
        ├── Swiper(260vp,5 个城市卡片,autoPlay + interval + loop + vertical)
        │   ├── 东京卡片(#FF6B6B)
        │   ├── 巴黎卡片(#722ED1)
        │   ├── 纽约卡片(#1677FF)
        │   ├── 罗马卡片(#FAAD14)
        │   └── 悉尼卡片(#52C41A)
        ├── 缩略图导航行(5 个 emoji 按钮,52×52vp)
        ├── 自动播放开关(开启/关闭 互斥按钮)
        ├── 轮播间隔选择(2s/3s/5s/8s 四选项)
        └── 播放模式(循环开关 + 横向/纵向切换)

代码约 230 行,核心聚焦 Swiper 的 index/autoPlay/interval/loop/vertical 五大属性,SwiperController 的 changeIndex 方法,以及 onChange 事件与外部 UI 的双向联动。

五、总结

本文以旅行目的地轮播为应用场景,深入解析了 ArkUI Swiper 组件的核心 API、控制器和交互实践。

回顾本文覆盖的核心要点:

  1. Swiper 的本质:声明式的滑动页面容器。将子组件放入 Swiper 中,每个子组件自动成为一个轮播页,Swiper 接管滑动手势、过渡动画和指示器渲染。

  2. SwiperController:命令式控制接口。changeIndex(idx, useAnimation?) 跳转到指定页,showNext/showPrevious 前进后退,finishAnimation 完成当前动画。

  3. index + onChange 的双向绑定:通过 @State currentIndex 建立 Swiper 与外部 UI(如缩略图导航)的联动机制——Swiper 的页面变化更新外部 UI,外部 UI 的点击操作通过 controller 或直接修改 index 控制 Swiper。

  4. autoPlay 与 interval 的协同autoPlay 控制"是否自动播",interval 控制"多久播一次"。两者正交,通过 .autoPlay(boolean).interval(milliseconds) 分别设置。AutoPlayOptions.stopWhenTouched 提供触摸暂停的精细控制。

  5. loop 循环与 vertical 方向loop(true) 实现无限循环(末页→首页、首页→末页),vertical(true) 实现纵向轮播。两者可通过 @State 动态切换。

  6. indicator 指示器true 使用默认圆点,false 隐藏,DotIndicator / DigitIndicator 提供自定义样式。

Swiper 是 ArkUI 中最有"产品感"的组件之一——它不是底层的布局工具,也不是基础的表单控件,而是一个直接面向用户的、完整的交互模式实现。从电商 Banner 到旅游目的地展示、从应用引导页到图片浏览器,Swiper 的灵活性和可控性让它几乎适用于任何需要"滑动浏览"的场景。当你需要在有限的屏幕空间内展示无限的内容时,Swiper 永远是最自然的选择。

Logo

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

更多推荐