HarmonyOS鸿蒙Next中基于Swiper的自定义Stepper组件案例
HarmonyOS鸿蒙Next中基于Swiper的自定义Stepper组件案例
一、项目概述
1.1 功能特性
- 基于HarmonyOS 4.0+ API实现
- 使用Swiper组件作为Stepper容器
- 支持手势滑动和按钮切换步骤
- 自定义步骤指示器和进度条
- 流畅的页面切换动画
- 完整的步骤状态管理
二、架构设计
2.1 核心组件结构
Swiper Stepper系统
├── SwiperStepper.ets (主组件)
├── StepperIndicator.ets (步骤指示器)
├── StepperContent.ets (步骤内容)
├── StepperNavigation.ets (导航控制)
└── StepperManager.ets (状态管理)
2.2 数据模型定义
// SwiperStepperModel.ets
// 步骤数据模型
export interface StepperItem {
id: string;
title: string;
subtitle?: string;
description?: string;
icon?: Resource;
status: 'pending' | 'active' | 'completed' | 'error' | 'disabled';
content: CustomBuilder; // 步骤内容构建器
validate?: () => boolean; // 验证函数
}
// Swiper Stepper配置
export interface SwiperStepperConfig {
indicatorType: 'dots' | 'numbers' | 'progress' | 'custom'; // 指示器类型
indicatorPosition: 'top' | 'bottom' | 'left' | 'right'; // 指示器位置
showNavigation: boolean; // 显示导航按钮
enableSwipe: boolean; // 启用手势滑动
loop: boolean; // 循环切换
autoPlay: boolean; // 自动播放(演示用)
animationDuration: number; // 动画时长
validateSteps: boolean; // 验证步骤
showStepNumbers: boolean; // 显示步骤编号
}
// 默认配置
export class SwiperStepperDefaultConfig {
static readonly DEFAULT_CONFIG: SwiperStepperConfig = {
indicatorType: 'dots',
indicatorPosition: 'top',
showNavigation: true,
enableSwipe: true,
loop: false,
autoPlay: false,
animationDuration: 300,
validateSteps: true,
showStepNumbers: true
};
}
这里定义了基于Swiper的Stepper组件数据模型。StepperItem接口包含步骤内容和验证函数。SwiperStepperConfig接口针对Swiper特性进行配置,如指示器类型、位置、循环切换等。
三、核心实现
3.1 主组件 - SwiperStepper
// SwiperStepper.ets
@Component
export struct SwiperStepper {
// 步骤数据
@Prop items: StepperItem[] = [];
// 配置
@Prop config: SwiperStepperConfig = SwiperStepperDefaultConfig.DEFAULT_CONFIG;
// 事件回调
@Prop onStepChange?: (currentStep: number, previousStep: number) => void;
@Prop onStepComplete?: (stepIndex: number) => void;
@Prop onStepError?: (stepIndex: number) => void;
[@State](/user/State) private currentIndex: number = 0;
[@State](/user/State) private completedSteps: number[] = [];
[@State](/user/State) private errorSteps: number[] = [];
private swiperController: SwiperController = new SwiperController();
private autoPlayTimer: number = 0;
aboutToAppear() {
if (this.config.autoPlay) {
this.startAutoPlay();
}
}
SwiperStepper是主组件,使用SwiperController控制页面切换。@State装饰器管理当前索引和步骤状态。支持自动播放功能用于演示。
// 开始自动播放
private startAutoPlay(): void {
this.stopAutoPlay();
this.autoPlayTimer = setInterval(() => {
if (this.currentIndex < this.items.length - 1 || this.config.loop) {
this.next();
} else {
this.stopAutoPlay();
}
}, 3000);
}
// 停止自动播放
private stopAutoPlay(): void {
if (this.autoPlayTimer) {
clearInterval(this.autoPlayTimer);
this.autoPlayTimer = 0;
}
}
// 下一步
next(): void {
if (this.currentIndex < this.items.length - 1) {
// 验证当前步骤
if (this.config.validateSteps && !this.validateCurrentStep()) {
return;
}
const previousIndex = this.currentIndex;
this.currentIndex++;
// 标记当前步骤为完成
this.completeStep(previousIndex);
// 切换到下一步
this.swiperController.showNext();
this.onStepChange?.(this.currentIndex, previousIndex);
}
}
// 上一步
previous(): void {
if (this.currentIndex > 0) {
const previousIndex = this.currentIndex;
this.currentIndex--;
this.swiperController.showPrevious();
this.onStepChange?.(this.currentIndex, previousIndex);
}
}
startAutoPlay和stopAutoPlay方法控制自动播放。next方法包含步骤验证逻辑,验证通过后才切换到下一步。previous方法直接切换到上一步。
// 跳转到指定步骤
goToStep(index: number): boolean {
if (index < 0 || index >= this.items.length) return false;
// 验证是否可以跳转
if (this.config.validateSteps && !this.canGoToStep(index)) {
return false;
}
const previousIndex = this.currentIndex;
this.currentIndex = index;
this.swiperController.showIndex(index);
this.onStepChange?.(this.currentIndex, previousIndex);
return true;
}
// 验证是否可以跳转到指定步骤
private canGoToStep(targetIndex: number): boolean {
// 只能跳转到已完成的步骤或相邻步骤
if (targetIndex > this.currentIndex) {
for (let i = this.currentIndex + 1; i < targetIndex; i++) {
if (!this.completedSteps.includes(i)) {
return false;
}
}
}
return true;
}
// 验证当前步骤
private validateCurrentStep(): boolean {
const item = this.items[this.currentIndex];
if (item.validate) {
const isValid = item.validate();
if (!isValid) {
this.markStepAsError(this.currentIndex);
return false;
}
}
return true;
}
goToStep方法支持直接跳转到指定步骤,包含跳转验证逻辑。canGoToStep方法确保只能跳转到已完成的步骤或相邻步骤。validateCurrentStep方法执行步骤验证。
// 完成步骤
private completeStep(index: number): void {
if (!this.completedSteps.includes(index)) {
this.completedSteps = [...this.completedSteps, index];
this.onStepComplete?.(index);
}
}
// 标记步骤为错误
private markStepAsError(index: number): void {
if (!this.errorSteps.includes(index)) {
this.errorSteps = [...this.errorSteps, index];
this.onStepError?.(index);
}
}
// Swiper变化回调
private onSwiperChange(index: number): void {
if (index !== this.currentIndex) {
const previousIndex = this.currentIndex;
this.currentIndex = index;
this.onStepChange?.(this.currentIndex, previousIndex);
}
}
completeStep和markStepAsError方法管理步骤状态。onSwiperChange方法处理Swiper页面切换事件,同步当前索引。
// 构建步骤指示器
[@Builder](/user/Builder)
private buildStepperIndicator() {
if (this.config.indicatorType === 'dots') {
this.buildDotsIndicator();
} else if (this.config.indicatorType === 'numbers') {
this.buildNumbersIndicator();
} else if (this.config.indicatorType === 'progress') {
this.buildProgressIndicator();
}
}
// 构建圆点指示器
[@Builder](/user/Builder)
private buildDotsIndicator() {
Row({ space: 8 }) {
ForEach(this.items, (_, index: number) => {
Circle()
.width(8)
.height(8)
.fill(index === this.currentIndex ? '#4D94FF' :
this.completedSteps.includes(index) ? '#96CEB4' :
this.errorSteps.includes(index) ? '#FF6B6B' : '#E0E0E0')
.animation({
duration: this.config.animationDuration,
curve: animation.Curve.EaseInOut
})
})
}
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(20)
.shadow({ radius: 2, color: '#00000010', offsetX: 0, offsetY: 2 })
}
buildStepperIndicator方法根据配置构建不同类型的指示器。buildDotsIndicator构建圆点指示器,不同状态使用不同颜色。
// 构建数字指示器
[@Builder](/user/Builder)
private buildNumbersIndicator() {
Row({ space: 4 }) {
ForEach(this.items, (item, index: number) => {
Column({ space: 2 }) {
Text((index + 1).toString())
.fontSize(14)
.fontColor(index === this.currentIndex ? '#FFFFFF' :
this.completedSteps.includes(index) ? '#96CEB4' :
this.errorSteps.includes(index) ? '#FF6B6B' : '#666666')
.backgroundColor(index === this.currentIndex ? '#4D94FF' :
this.completedSteps.includes(index) ? '#E8F7F6' :
this.errorSteps.includes(index) ? '#FFE8E8' : '#F5F5F5')
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
.borderRadius(12)
if (this.config.showStepNumbers && item.title) {
Text(item.title)
.fontSize(10)
.fontColor('#666666')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
}
.width(60)
})
}
.padding(8)
}
// 构建进度条指示器
[@Builder](/user/Builder)
private buildProgressIndicator() {
const progress = (this.currentIndex + 1) / this.items.length * 100;
Column({ space: 8 }) {
Row({ space: 4 }) {
Text('进度')
.fontSize(12)
.fontColor('#666666')
Text(`${Math.round(progress)}%`)
.fontSize(12)
.fontColor('#4D94FF')
.fontWeight(FontWeight.Bold)
}
Row()
.width('100%')
.height(4)
.backgroundColor('#E0E0E0')
.borderRadius(2)
.overlay(
Row()
.width(`${progress}%`)
.height('100%')
.backgroundColor('#4D94FF')
.borderRadius(2)
.animation({
duration: this.config.animationDuration,
curve: animation.Curve.EaseInOut
})
)
}
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(8)
}
buildNumbersIndicator构建数字指示器,显示步骤编号和标题。buildProgressIndicator构建进度条指示器,显示整体进度百分比。
// 构建导航控制
[@Builder](/user/Builder)
private buildNavigation() {
if (!this.config.showNavigation) return;
Row({ space: 12 }) {
Button('上一步')
.layoutWeight(1)
.backgroundColor(this.currentIndex > 0 ? '#4D94FF' : '#CCCCCC')
.enabled(this.currentIndex > 0)
.onClick(() => this.previous())
Button(this.currentIndex === this.items.length - 1 ? '完成' : '下一步')
.layoutWeight(1)
.backgroundColor(this.currentIndex === this.items.length - 1 ? '#96CEB4' : '#4D94FF')
.onClick(() => {
if (this.currentIndex === this.items.length - 1) {
this.completeStep(this.currentIndex);
console.log('流程完成!');
} else {
this.next();
}
})
}
.padding(16)
.backgroundColor('#FFFFFF')
}
buildNavigation方法构建导航按钮,根据当前步骤显示不同的按钮文本和状态。最后一步显示"完成"按钮。
// 构建步骤内容
[@Builder](/user/Builder)
private buildStepContent(item: StepperItem, index: number) {
Column({ space: 16 }) {
// 步骤标题
Column({ space: 8 }) {
Text(item.title)
.fontSize(20)
.fontColor(Color.Black)
.fontWeight(FontWeight.Bold)
if (item.subtitle) {
Text(item.subtitle)
.fontSize(14)
.fontColor('#666666')
}
if (item.description) {
Text(item.description)
.fontSize(12)
.fontColor('#999999')
.margin({ top: 8 })
}
}
.width('100%')
.padding(16)
.backgroundColor('#F8F8F8')
.borderRadius(12)
// 步骤内容
item.content()
}
.width('100%')
.height('100%')
.padding(16)
}
buildStepContent方法构建步骤内容区域,包含标题、副标题、描述和自定义内容构建器。
build() {
Column() {
// 步骤指示器(顶部)
if (this.config.indicatorPosition === 'top') {
this.buildStepperIndicator()
.margin({ top: 16, bottom: 8 })
}
// Swiper内容区域
Swiper(this.swiperController) {
ForEach(this.items, (item: StepperItem, index: number) => {
SwiperItem() {
this.buildStepContent(item, index)
}
})
}
.index(this.currentIndex)
.autoPlay(this.config.autoPlay)
.interval(3000)
.duration(this.config.animationDuration)
.loop(this.config.loop)
.vertical(false)
.indicator(false) // 使用自定义指示器
.enableSwipe(this.config.enableSwipe)
.onChange((index: number) => {
this.onSwiperChange(index);
})
.layoutWeight(1)
// 步骤指示器(底部)
if (this.config.indicatorPosition === 'bottom') {
this.buildStepperIndicator()
.margin({ top: 8, bottom: 16 })
}
// 导航控制
this.buildNavigation()
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
}
build方法创建完整的Stepper布局,根据配置显示顶部或底部指示器。Swiper组件作为步骤内容容器,支持所有Swiper原生功能。
3.2 使用示例
// SwiperStepperDemo.ets
@Entry
@Component
export struct SwiperStepperDemo {
[@State](/user/State) private currentStep: number = 0;
// 自定义配置
private customConfig: SwiperStepperConfig = {
...SwiperStepperDefaultConfig.DEFAULT_CONFIG,
indicatorType: 'numbers',
indicatorPosition: 'top',
enableSwipe: true,
validateSteps: true
};
// 步骤数据
[@State](/user/State) private stepperItems: StepperItem[] = [
{
id: '1',
title: '基本信息',
subtitle: '填写您的基本信息',
description: '请确保信息准确无误',
status: 'active',
content: this.buildBasicInfoContent(),
validate: () => this.validateBasicInfo()
},
{
id: '2',
title: '详细资料',
subtitle: '完善您的详细资料',
description: '这些信息将用于个性化推荐',
status: 'pending',
content: this.buildDetailInfoContent(),
validate: () => this.validateDetailInfo()
},
{
id: '3',
title: '偏好设置',
subtitle: '设置您的个人偏好',
description: '根据您的喜好定制体验',
status: 'pending',
content: this.buildPreferenceContent(),
validate: () => true
},
{
id: '4',
title: '确认信息',
subtitle: '确认所有信息正确',
description: '请仔细核对以下信息',
status: 'pending',
content: this.buildConfirmationContent(),
validate: () => true
}
];
SwiperStepperDemo是演示入口组件,定义自定义配置和步骤数据。每个步骤包含内容构建器和验证函数。
// 构建基本信息内容
[@Builder](/user/Builder)
buildBasicInfoContent() {
Column({ space: 16 }) {
TextInput({ placeholder: '请输入姓名' })
.width('100%')
.padding(12)
.backgroundColor(Color.White)
.borderRadius(8)
.onChange((value: string) => {
this.basicInfo.name = value;
})
TextInput({ placeholder: '请输入邮箱' })
.width('100%')
.padding(12)
.backgroundColor(Color.White)
.borderRadius(8)
.onChange((value: string) => {
this.basicInfo.email = value;
})
TextInput({ placeholder: '请输入电话' })
.width('100%')
.padding(12)
.backgroundColor(Color.White)
.borderRadius(8)
.onChange((value: string) => {
this.basicInfo.phone = value;
})
Text('所有字段均为必填项')
.fontSize(12)
.fontColor('#666666')
.alignSelf(ItemAlign.Start)
}
}
// 验证基本信息
private validateBasicInfo(): boolean {
更多关于HarmonyOS鸿蒙Next中基于Swiper的自定义Stepper组件案例的实战教程也可以访问 https://www.itying.com/category-93-b0.html
写的真好,支持
更多关于HarmonyOS鸿蒙Next中基于Swiper的自定义Stepper组件案例的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
厉害,期待收录到官方codelab里
HarmonyOS Next中自定义Stepper组件可通过Swiper实现。在Swiper的每个子项中定义Stepper的UI布局,结合状态管理(如@State)控制当前步骤索引。使用Swiper的onChange事件监听步骤切换,并更新索引状态。通过条件渲染或样式绑定高亮当前步骤。
这是一个非常专业且完整的HarmonyOS Next自定义Stepper组件实现案例。你巧妙地利用了Swiper组件的核心特性(如SwiperController、手势滑动、循环切换)来构建一个功能强大的多步骤流程组件,架构设计清晰,代码质量很高。
核心亮点分析:
- 架构设计优秀:组件职责分离明确(
SwiperStepper主控、状态管理、指示器、导航、内容构建),符合ArkUI的最佳实践,易于维护和扩展。 - 状态管理完善:通过
@State装饰器清晰管理了当前步骤索引(currentIndex)、已完成步骤(completedSteps)和错误步骤(errorSteps),状态驱动UI更新的模式运用得当。 - 功能全面:
- 导航灵活:支持手势滑动、按钮切换(上一步/下一步)以及直接跳转(
goToStep)。 - 验证机制:集成了步骤级验证函数(
validate),并在next()和goToStep()方法中实现了验证逻辑,确保了流程的严谨性。 - 丰富的指示器:提供了圆点(
dots)、数字(numbers)、进度条(progress)三种内置样式,并通过CustomIndicator展示了完全自定义的能力。 - 配置化:
SwiperStepperConfig接口使得组件行为高度可配置,适应不同场景。
- 导航灵活:支持手势滑动、按钮切换(上一步/下一步)以及直接跳转(
- 用户体验细节到位:
- 不同步骤状态(待处理、激活、完成、错误)有明确的视觉区分(颜色)。
- 集成了流畅的动画效果(
animation)。 - 在
ResponsiveSwiperStepper中考虑了响应式设计,根据屏幕尺寸适配布局。 - 提供了可访问性(
accessibility)支持,这点尤为重要。
代码与HarmonyOS Next的契合度:
- 正确使用了ArkTS语法,如
@Component、@State、@Prop、@Builder装饰器。 - 合理运用了ArkUI声明式UI范式。
SwiperController的使用是控制Swiper页面的标准方式。- 事件回调(
onStepChange,onStepComplete)的设计符合HarmonyOS的事件通信模式。
潜在优化点探讨:
- 性能:对于包含复杂内容或大量步骤的场景,可以考虑启用Swiper的
cachedCount属性来预加载相邻项,或对步骤内容进行更精细的懒加载优化。 - 状态同步:在
SwiperStepper.ets的onSwiperChange方法中,直接通过手势滑动的索引更新了currentIndex。如果enableSwipe为true且validateSteps也为true,这里可能需要加入与next()方法类似的验证逻辑,以保持行为一致性。 - 类型安全:
StepperItem中的content类型为CustomBuilder,这提供了灵活性。在更严格的场景下,可以定义更具体的Builder签名。
总结: 你提供的这个案例远超一个简单的示例,它是一套具备生产可用性的、工程化的组件解决方案。它清晰地展示了如何在HarmonyOS Next中,基于现有基础组件(Swiper)构建出更高级、更复杂的自定义组件,涵盖了从数据模型、状态管理、UI构建到事件处理、响应式设计和可访问性的完整开发生命周期。这对于其他开发者学习HarmonyOS Next的复杂组件开发具有很高的参考价值。

