鸿蒙新特性——Swiper 轮播图深度解析
一、引言
轮播图(Carousel)是移动应用中最常见的视觉组件之一。从电商首页的促销 Banner、社交应用的图片浏览、到旅游 App 的目的地推荐——Swiper 以有限的空间承载了无限的内容浏览能力。用户通过左右滑动手势在页面之间自由切换,配合指示器(indicator)感知当前位置和总页数,配合自动播放(autoPlay)实现"不操作也能看"的被动浏览体验。
ArkUI 提供了原生的 Swiper 组件,将轮播这种复杂的交互模式封装为一个声明式的容器组件。开发者只需将子组件放入 Swiper 内部,每个子组件自动成为一个轮播页,Swiper 接管所有滑动手势、过渡动画、边界处理和指示器渲染。通过 SwiperController,你可以用代码控制"跳转到第 N 页"、“下一页”、“上一页”;通过 autoPlay 和 interval,你可以配置自动播放的行为;通过 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 属性承担两个角色:
- 读取:当前显示的页面索引(从 0 开始)
- 写入:设置索引来跳转到指定页面
.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 页面整体设计
旅行目的地轮播围绕"浏览旅游城市"的场景设计,包含以下功能模块:
- 主轮播区(260vp 高度)— 5 个城市卡片,每张有独特的底色(红/紫/蓝/金/绿)+ emoji + 城市名/国家/描述
- 缩略图导航行 — 5 个小图标按钮,点击跳转到对应轮播页
- 自动播放开关 — 开启/关闭两个按钮互斥选择
- 轮播间隔选择 — 2s / 3s / 5s / 8s 四个选项
- 播放模式 — 循环开关 + 横向/纵向切换
整体的设计语言是"沉浸式浏览"——主轮播使用大面积的纯色背景 + 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、控制器和交互实践。
回顾本文覆盖的核心要点:
-
Swiper 的本质:声明式的滑动页面容器。将子组件放入 Swiper 中,每个子组件自动成为一个轮播页,Swiper 接管滑动手势、过渡动画和指示器渲染。
-
SwiperController:命令式控制接口。
changeIndex(idx, useAnimation?)跳转到指定页,showNext/showPrevious前进后退,finishAnimation完成当前动画。 -
index + onChange 的双向绑定:通过
@State currentIndex建立 Swiper 与外部 UI(如缩略图导航)的联动机制——Swiper 的页面变化更新外部 UI,外部 UI 的点击操作通过 controller 或直接修改 index 控制 Swiper。 -
autoPlay 与 interval 的协同:
autoPlay控制"是否自动播",interval控制"多久播一次"。两者正交,通过.autoPlay(boolean)和.interval(milliseconds)分别设置。AutoPlayOptions.stopWhenTouched提供触摸暂停的精细控制。 -
loop 循环与 vertical 方向:
loop(true)实现无限循环(末页→首页、首页→末页),vertical(true)实现纵向轮播。两者可通过 @State 动态切换。 -
indicator 指示器:
true使用默认圆点,false隐藏,DotIndicator/DigitIndicator提供自定义样式。
Swiper 是 ArkUI 中最有"产品感"的组件之一——它不是底层的布局工具,也不是基础的表单控件,而是一个直接面向用户的、完整的交互模式实现。从电商 Banner 到旅游目的地展示、从应用引导页到图片浏览器,Swiper 的灵活性和可控性让它几乎适用于任何需要"滑动浏览"的场景。当你需要在有限的屏幕空间内展示无限的内容时,Swiper 永远是最自然的选择。
更多推荐



所有评论(0)