前言
轮播图在每个项目中都很常见,鸿蒙中在容器组件中也提供了Swiper组件,用于子组件滑动轮播显示,和前端的使用起来也是异曲同工,我们先看下基本的用法。
Swiper() { ForEach(["1", "2", "3", "4", "5", "6"], (item: string) => { Text(item.toString()) .width('90%') .height(160) .backgroundColor(0xAFEEEE) .textAlign(TextAlign.Center) .fontSize(30) }, (item: string) => item) }
以上的代码便轻松的实现了一个轮播图效果,当然了,只是一个简单的案例,很多属性并没有设置,按照正常的使用而言,确实没必要再搞什么封装,但是,有一个潜在的问题是需要封装的,比如使用懒加载数据的时候,不封装的话,每实现一个轮播图就需要重复大量的代码,这显然是冗余的;还有一种场景,那就是,系统的轮播无法满足我们的需求,这种情况下,是不得不进行封装的。
本文的大致内容如下:
1、简单封装之后的代码及效果展示
2、基于Swiper进行懒加载数据和普通数据封装
3、开源地址
4、相关总结
一、简单封装之后的代码及效果展示
封装的Banner已经上传到了远程仓库,使用起来也是非常的简单
方式一:在Terminal窗口中,执行如下命令安装三方包,DevEco Studio会自动在工程的oh-package.json5中自动添加三方包依赖。
ohpm install /banner
方式二:在工程的oh-package.json5中设置三方包依赖,配置示例如下:
"dependencies": { "@abner/banner": "^1.0.2"}
效果没什么好说的,都是用Swiper组件所封装的。
代码实现上,毕竟采取了封装,简化了大量的代码,简单的案例如下:
Banner({ data: ["1", "2", "3", "4", "5", "6"], itemPage: this.itemPage })
更多的案例,就不贴了,直接去看第3项中的开源地址即可。
相关属性配置
属性 |
类型 |
概述 |
data |
Array<Object> |
数据源 |
itemPage |
(index: number, item: Object) |
banner对应的页面 |
onChange |
回调函数 |
条目切换监听 |
bannerHeight |
Length |
banner高度 |
bannerWidth |
Length |
banner宽度 |
autoPlay |
boolean |
是否自动播放,默认false |
interval |
number |
默认3秒轮播一次 |
disableSwipe |
boolean |
是否禁止滑动 |
itemSpace |
number |
子组件之间的间隙 |
currentIndex |
number |
选中,默认第0个 |
indicator |
DotIndicator | DigitIndicator | boolean |
指示器 |
isLineIndicator |
boolean |
是否是自定义的线条指示器 |
indicatorType |
IndicatorType |
指示器位置 |
lineIndicatorWidth |
number |
线条指示器宽度 |
lineIndicatorHeight |
number |
线条指示器高度 |
lineIndicatorBgColor |
ResourceColor |
线条指示器背景 |
lineMargin |
Margin | Length |
线条指示器边距 |
isLoop |
boolean |
是否开启循环,默认是循环 |
indicatorRules |
Record<string, Record<string, string | VerticalAlign | HorizontalAlign>> |
线条指示器位置 |
isLazyData |
boolean |
是否使用数据懒加载 |
lazyCachedCount |
number |
缓存条目数量,默认是1 |
onLazyDataSource |
(dataSource: BannerDataSource) |
回调函数,用于控制数据的增删 |
二、基于Swiper进行懒加载数据和普通数据封装
首先Swiper的子组件是支持ForEach和LazyForEach进行渲染数据的,LazyForEach也就是数据懒加载模式,也是官方案例中默认推荐的模式,当组件滑出可视区域外时,框架会进行组件销毁回收以降低内存占用,但是两种渲染数据,在代码逻辑上是完全不同的。
ForEach就比较的简单,数据源是一个数组,在封装上也是非常的简洁:
Swiper(this.swiperController) { ForEach(this.data, (item: Object, index: number) => { this.itemPage(index, item) }) }
LazyForEach模式,使用起来相对复杂,组件的创建包括两种情况:LazyForEach首次渲染和LazyForEach非首次渲染,这些都是需要考虑的。
ForEach数据加载,我们只考虑数据源的变化即可,但在LazyForEach中,必须使用DataChangeListener对象来进行更新,需要我们创建新的对象,实现IDataSource,进行数据的增删改查。
/** * AUTHOR:AbnerMing * DATE:2024/2/23 * INTRODUCE:懒加载数据 * */ export class BannerDataSource implements IDataSource { private listeners: DataChangeListener[] = [] private originDataArray: Object[] = [] /** * AUTHOR:AbnerMing * INTRODUCE:返回列表数量 * */ totalCount(): number { return this.originDataArray.length } /** * AUTHOR:AbnerMing * INTRODUCE:返回某一个对象 * */ getData(index: number): Object { return [this.originDataArray[index]] } /** * AUTHOR:AbnerMing * INTRODUCE:该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听 * */ registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { this.listeners.push(listener); } } /** * AUTHOR:AbnerMing * INTRODUCE:该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听 * */ unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { this.listeners.splice(pos, 1); } } // 通知LazyForEach组件需要重载所有子组件 notifyDataReload(): void { this.listeners.forEach(listener => { listener.onDataReloaded(); }) } // 通知LazyForEach组件需要在index对应索引处添加子组件 notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index); }) } // 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件 notifyDataChange(index: number): void { this.listeners.forEach(listener => { listener.onDataChange(index); }) } // 通知LazyForEach组件需要在index对应索引处删除该子组件 notifyDataDelete(index: number): void { this.listeners.forEach(listener => { listener.onDataDelete(index); }) } // 通知LazyForEach组件将from索引和to索引处的子组件进行交换 notifyDataMove(from: number, to: number): void { this.listeners.forEach(listener => { listener.onDataMove(from, to); }) } //初始化数据 public addData(index: number, data: Object): void { this.originDataArray.splice(index, 0, data); this.notifyDataAdd(index); } //追加数据 public pushData(data: Object): void { this.originDataArray.push(data); this.notifyDataAdd(this.originDataArray.length - 1); } //删除数据 public deleteData(index: number): void { this.originDataArray.splice(index, 1); this.notifyDataDelete(index); } //交换数据 public moveData(from: number, to: number): void { let temp: Object = this.originDataArray[from]; this.originDataArray[from] = this.originDataArray[to]; this.originDataArray[to] = temp; this.notifyDataMove(from, to); } /** * AUTHOR:AbnerMing * INTRODUCE:改变单个数据 * */ public changeData(index: number, data: Object): void { this.originDataArray.splice(index, 1, data); this.notifyDataChange(index); } //重置所有子组件的index索引 public reloadData(): void { this.notifyDataReload(); } }
对于以上封装之后,在使用上需要注意,也就是更新数据的时候:
声明变量:
@State bannerDataSource: BannerDataSource = new BannerDataSource()
赋值变量:
Banner({ data: ["1", "2", "3", "4", "5", "6"], itemPage: this.itemPage, onLazyDataSource: (dataSource: BannerDataSource) => { this.bannerDataSource=dataSource } })
行为操作:
this.bannerDataSource.pushData()//追加数据 this.bannerDataSource.deleteData()//删除数据
三、开源地址
在开源地址中,对各个案例的使用方式,也做了相信的介绍。
https://ohpm.openharmony.cn/#/cn/detail/@abner%2Fbanner
四、相关总结
目前的轮播图,仅仅对Swiper做了简单的封装,另外增加了一个线条指示器,这远远是不够的,毕竟日常的轮播图形式多种多样,指示器也是千奇百怪,后续也会在此基础之上进行不断的扩展。