• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 创建轮播 (Swiper)
2<!--Kit: ArkUI-->
3<!--Subsystem: ArkUI-->
4<!--Owner: @Hu_ZeQi-->
5<!--Designer: @jiangdayuan-->
6<!--Tester: @lxl007-->
7<!--Adviser: @HelloCrease-->
8
9
10[Swiper](../reference/apis-arkui/arkui-ts/ts-container-swiper.md)组件提供滑动轮播显示的能力。Swiper本身是一个容器组件,当设置了多个子组件后,可以对这些子组件进行轮播显示。通常,在一些应用首页显示推荐的内容时,需要用到轮播显示的能力。
11
12针对复杂页面场景,可以使用 Swiper 组件的预加载机制,利用主线程的空闲时间来提前构建和布局绘制组件,优化滑动体验。<!--Del-->详细指导见[Swiper高性能开发指导](../performance/swiper_optimization.md)。<!--DelEnd-->
13
14
15## 布局与约束
16
17Swiper作为一个容器组件,如果设置了自身尺寸属性,则在轮播显示过程中均以该尺寸生效。如果自身尺寸属性未被设置,则分两种情况:如果设置了prevMargin或者nextMargin属性,则Swiper自身尺寸会跟随其父组件;如果未设置prevMargin或者nextMargin属性,则会自动根据子组件的大小设置自身的尺寸。
18
19
20## 循环播放
21
22通过loop属性控制是否循环播放,该属性默认值为true。
23
24当loop为true时,在显示第一页或最后一页时,可以继续往前切换到前一页或者往后切换到后一页。如果loop为false,则在第一页或最后一页时,无法继续向前或者向后切换页面。
25
26- loop为true
27
28```ts
29Swiper() {
30  Text('0')
31    .width('90%')
32    .height('100%')
33    .backgroundColor(Color.Gray)
34    .textAlign(TextAlign.Center)
35    .fontSize(30)
36
37  Text('1')
38    .width('90%')
39    .height('100%')
40    .backgroundColor(Color.Green)
41    .textAlign(TextAlign.Center)
42    .fontSize(30)
43
44  Text('2')
45    .width('90%')
46    .height('100%')
47    .backgroundColor(Color.Pink)
48    .textAlign(TextAlign.Center)
49    .fontSize(30)
50}
51.loop(true)
52```
53
54![loop_true](figures/loop_true.gif)
55
56- loop为false
57
58```ts
59Swiper() {
60  // ...
61}
62.loop(false)
63```
64
65![loop_false](figures/loop_false.gif)
66
67
68## 自动轮播
69
70Swiper通过设置autoPlay属性,控制是否自动轮播子组件。该属性默认值为false。
71
72autoPlay为true时,会自动切换播放子组件,子组件与子组件之间的播放间隔通过interval属性设置。interval属性默认值为3000,单位毫秒。
73
74```ts
75Swiper() {
76  // ...
77}
78.loop(true)
79.autoPlay(true)
80.interval(1000)
81```
82
83![autoPlay](figures/autoPlay.gif)
84
85
86## 导航点样式
87
88Swiper提供了默认的导航点样式和导航点箭头样式,导航点默认显示在Swiper下方居中位置,开发者也可以通过indicator属性自定义导航点的位置和样式,导航点箭头默认不显示。
89
90通过indicator属性,开发者可以设置导航点相对于Swiper组件上下左右四个方位的位置,同时也可以设置每个导航点的尺寸、颜色、蒙层和被选中导航点的颜色。
91
92- 导航点使用默认样式
93
94```ts
95Swiper() {
96  Text('0')
97    .width('90%')
98    .height('100%')
99    .backgroundColor(Color.Gray)
100    .textAlign(TextAlign.Center)
101    .fontSize(30)
102
103  Text('1')
104    .width('90%')
105    .height('100%')
106    .backgroundColor(Color.Green)
107    .textAlign(TextAlign.Center)
108    .fontSize(30)
109
110  Text('2')
111    .width('90%')
112    .height('100%')
113    .backgroundColor(Color.Pink)
114    .textAlign(TextAlign.Center)
115    .fontSize(30)
116}
117```
118
119![indicator](figures/indicator.PNG)
120
121- 自定义导航点样式
122
123导航点直径设为30vp,左边距为0,导航点颜色设为红色。
124
125```ts
126Swiper() {
127  // ...
128}
129.indicator(
130  Indicator.dot()
131    .left(0)
132    .itemWidth(15)
133    .itemHeight(15)
134    .selectedItemWidth(30)
135    .selectedItemHeight(15)
136    .color(Color.Red)
137    .selectedColor(Color.Blue)
138)
139```
140
141![ind](figures/ind.PNG)
142
143Swiper通过设置[displayArrow](../reference/apis-arkui/arkui-ts/ts-container-swiper.md#displayarrow10)属性,可以控制导航点箭头的大小、位置、颜色,底板的大小及颜色,以及鼠标悬停时是否显示箭头。
144
145- 箭头使用默认样式
146
147```ts
148Swiper() {
149  // ...
150}
151.displayArrow(true, false)
152```
153
154![arrow1](figures/arrow1.gif)
155
156- 自定义箭头样式
157
158箭头显示在组件两侧,大小为18vp,导航点箭头颜色设为蓝色。
159
160```ts
161Swiper() {
162  // ...
163}
164.displayArrow({
165  showBackground: true,
166  isSidebarMiddle: true,
167  backgroundSize: 24,
168  backgroundColor: Color.White,
169  arrowSize: 18,
170  arrowColor: Color.Blue
171  }, false)
172```
173
174![arrow2](figures/arrow2.gif)
175
176## 页面切换方式
177
178Swiper支持手指滑动、点击导航点和通过控制器三种方式切换页面,以下示例展示通过控制器切换页面的方法。
179
180```ts
181@Entry
182@Component
183struct SwiperDemo {
184  private swiperBackgroundColors: Color[] = [Color.Blue, Color.Brown, Color.Gray, Color.Green, Color.Orange,
185    Color.Pink, Color.Red, Color.Yellow];
186  private swiperAnimationMode: (SwiperAnimationMode | boolean | undefined)[] = [undefined, true, false,
187    SwiperAnimationMode.NO_ANIMATION, SwiperAnimationMode.DEFAULT_ANIMATION, SwiperAnimationMode.FAST_ANIMATION];
188  private swiperController: SwiperController = new SwiperController();
189  private animationModeIndex: number = 0;
190  private animationMode: (SwiperAnimationMode | boolean | undefined) = undefined;
191  @State animationModeStr: string = 'undefined';
192  @State targetIndex: number = 0;
193
194  aboutToAppear(): void {
195    this.toSwiperAnimationModeStr();
196  }
197
198  build() {
199    Column({ space: 5 }) {
200      Swiper(this.swiperController) {
201        ForEach(this.swiperBackgroundColors, (backgroundColor: Color, index: number) => {
202          Text(index.toString())
203            .width(250)
204            .height(250)
205            .backgroundColor(backgroundColor)
206            .textAlign(TextAlign.Center)
207            .fontSize(30)
208        })
209      }
210      .indicator(true)
211
212      Row({ space: 12 }) {
213        Button('showNext')
214          .onClick(() => {
215            this.swiperController.showNext(); // 通过controller切换到后一页
216          })
217        Button('showPrevious')
218          .onClick(() => {
219            this.swiperController.showPrevious(); // 通过controller切换到前一页
220          })
221      }.margin(5)
222
223      Row({ space: 12 }) {
224        Text('Index:')
225        Button(this.targetIndex.toString())
226          .onClick(() => {
227            this.targetIndex = (this.targetIndex + 1) % this.swiperBackgroundColors.length;
228          })
229      }.margin(5)
230      Row({ space: 12 }) {
231        Text('AnimationMode:')
232        Button(this.animationModeStr)
233          .onClick(() => {
234            this.animationModeIndex = (this.animationModeIndex + 1) % this.swiperAnimationMode.length;
235            this.toSwiperAnimationModeStr();
236          })
237      }.margin(5)
238
239      Row({ space: 12 }) {
240        Button('changeIndex(' + this.targetIndex + ', ' + this.animationModeStr + ')')
241          .onClick(() => {
242            this.swiperController.changeIndex(this.targetIndex, this.animationMode); // 通过controller切换到指定页
243          })
244      }.margin(5)
245    }.width('100%')
246    .margin({ top: 5 })
247  }
248
249  private toSwiperAnimationModeStr() {
250    this.animationMode = this.swiperAnimationMode[this.animationModeIndex];
251    if ((this.animationMode === true) || (this.animationMode === false)) {
252      this.animationModeStr = '' + this.animationMode;
253    } else if ((this.animationMode === SwiperAnimationMode.NO_ANIMATION) ||
254      (this.animationMode === SwiperAnimationMode.DEFAULT_ANIMATION) ||
255      (this.animationMode === SwiperAnimationMode.FAST_ANIMATION)) {
256      this.animationModeStr = SwiperAnimationMode[this.animationMode];
257    } else {
258      this.animationModeStr = 'undefined';
259    }
260  }
261}
262```
263
264![controll](figures/controll.gif)
265
266
267## 轮播方向
268
269Swiper支持水平和垂直方向上进行轮播,主要通过vertical属性控制。
270
271当vertical为true时,表示在垂直方向上进行轮播;为false时,表示在水平方向上进行轮播。vertical默认值为false。
272
273
274- 设置水平方向上轮播。
275
276```ts
277Swiper() {
278  // ...
279}
280.indicator(true)
281.vertical(false)
282```
283
284
285![截图2](figures/截图2.PNG)
286
287
288- 设置垂直方向轮播。
289
290```ts
291Swiper() {
292  // ...
293}
294.indicator(true)
295.vertical(true)
296```
297
298
299![截图3](figures/截图3.PNG)
300
301
302## 每页显示多个子页面
303
304Swiper支持在一个页面内同时显示多个子组件,通过[displayCount](../reference/apis-arkui/arkui-ts/ts-container-swiper.md#displaycount8)属性设置。
305
306```ts
307Swiper() {
308  Text('0')
309    .width(250)
310    .height(250)
311    .backgroundColor(Color.Gray)
312    .textAlign(TextAlign.Center)
313    .fontSize(30)
314  Text('1')
315    .width(250)
316    .height(250)
317    .backgroundColor(Color.Green)
318    .textAlign(TextAlign.Center)
319    .fontSize(30)
320  Text('2')
321    .width(250)
322    .height(250)
323    .backgroundColor(Color.Pink)
324    .textAlign(TextAlign.Center)
325    .fontSize(30)
326  Text('3')
327    .width(250)
328    .height(250)
329    .backgroundColor(Color.Blue)
330    .textAlign(TextAlign.Center)
331    .fontSize(30)
332}
333.indicator(true)
334.displayCount(2)
335```
336
337![two](figures/two.PNG)
338
339## 自定义切换动画
340
341Swiper支持通过[customContentTransition](../reference/apis-arkui/arkui-ts/ts-container-swiper.md#customcontenttransition12)设置自定义切换动画,可以在回调中对视窗内所有页面逐帧设置透明度、缩放比例、位移、渲染层级等属性实现自定义切换动画。
342
343```ts
344@Entry
345@Component
346struct SwiperCustomAnimationExample {
347  private DISPLAY_COUNT: number = 2;
348  private MIN_SCALE: number = 0.75;
349
350  @State backgroundColors: Color[] = [Color.Green, Color.Blue, Color.Yellow, Color.Pink, Color.Gray, Color.Orange];
351  @State opacityList: number[] = [];
352  @State scaleList: number[] = [];
353  @State translateList: number[] = [];
354  @State zIndexList: number[] = [];
355
356  aboutToAppear(): void {
357    for (let i = 0; i < this.backgroundColors.length; i++) {
358      this.opacityList.push(1.0);
359      this.scaleList.push(1.0);
360      this.translateList.push(0.0);
361      this.zIndexList.push(0);
362    }
363  }
364
365  build() {
366    Column() {
367      Swiper() {
368        ForEach(this.backgroundColors, (backgroundColor: Color, index: number) => {
369          Text(index.toString()).width('100%').height('100%').fontSize(50).textAlign(TextAlign.Center)
370            .backgroundColor(backgroundColor)
371            .opacity(this.opacityList[index])
372            .scale({ x: this.scaleList[index], y: this.scaleList[index] })
373            .translate({ x: this.translateList[index] })
374            .zIndex(this.zIndexList[index])
375        })
376      }
377      .height(300)
378      .indicator(false)
379      .displayCount(this.DISPLAY_COUNT, true)
380      .customContentTransition({
381        timeout: 1000,
382        transition: (proxy: SwiperContentTransitionProxy) => {
383          if (proxy.position <= proxy.index % this.DISPLAY_COUNT || proxy.position >= this.DISPLAY_COUNT + proxy.index % this.DISPLAY_COUNT) {
384            // 同组页面完全滑出视窗外时,重置属性值
385            this.opacityList[proxy.index] = 1.0;
386            this.scaleList[proxy.index] = 1.0;
387            this.translateList[proxy.index] = 0.0;
388            this.zIndexList[proxy.index] = 0;
389          } else {
390            // 同组页面未滑出视窗外时,对同组中左右两个页面,逐帧根据position修改属性值
391            if (proxy.index % this.DISPLAY_COUNT === 0) {
392              this.opacityList[proxy.index] = 1 - proxy.position / this.DISPLAY_COUNT;
393              this.scaleList[proxy.index] = this.MIN_SCALE + (1 - this.MIN_SCALE) * (1 - proxy.position / this.DISPLAY_COUNT);
394              this.translateList[proxy.index] = - proxy.position * proxy.mainAxisLength + (1 - this.scaleList[proxy.index]) * proxy.mainAxisLength / 2.0;
395            } else {
396              this.opacityList[proxy.index] = 1 - (proxy.position - 1) / this.DISPLAY_COUNT;
397              this.scaleList[proxy.index] = this.MIN_SCALE + (1 - this.MIN_SCALE) * (1 - (proxy.position - 1) / this.DISPLAY_COUNT);
398              this.translateList[proxy.index] = - (proxy.position - 1) * proxy.mainAxisLength - (1 - this.scaleList[proxy.index]) * proxy.mainAxisLength / 2.0;
399            }
400            this.zIndexList[proxy.index] = -1;
401          }
402        }
403      })
404    }.width('100%')
405  }
406}
407```
408
409![customAnimation](figures/swiper-custom-animation.gif)
410
411## Swiper与Tabs联动
412
413Swiper选中的元素改变时,会通过onSelected回调事件,将元素的索引值index返回。通过调用tabsController.changeIndex(index)方法来实现Tabs页签的切换。
414
415```ts
416// xxx.ets
417class MyDataSource implements IDataSource {
418  private list: number[] = [];
419
420  constructor(list: number[]) {
421    this.list = list;
422  }
423
424  totalCount(): number {
425    return this.list.length;
426  }
427
428  getData(index: number): number {
429    return this.list[index];
430  }
431
432  registerDataChangeListener(listener: DataChangeListener): void {
433  }
434
435  unregisterDataChangeListener() {
436  }
437}
438
439@Entry
440@Component
441struct TabsSwiperExample {
442  @State fontColor: string = '#182431';
443  @State selectedFontColor: string = '#007DFF';
444  @State currentIndex: number = 0;
445  private list: number[] = [];
446  private tabsController: TabsController = new TabsController();
447  private swiperController: SwiperController = new SwiperController();
448  private swiperData: MyDataSource = new MyDataSource([]);
449
450  aboutToAppear(): void {
451    for (let i = 0; i <= 9; i++) {
452      this.list.push(i);
453    }
454    this.swiperData = new MyDataSource(this.list);
455  }
456
457  @Builder tabBuilder(index: number, name: string) {
458    Column() {
459      Text(name)
460        .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor)
461        .fontSize(16)
462        .fontWeight(this.currentIndex === index ? 500 : 400)
463        .lineHeight(22)
464        .margin({ top: 17, bottom: 7 })
465      Divider()
466        .strokeWidth(2)
467        .color('#007DFF')
468        .opacity(this.currentIndex === index ? 1 : 0)
469    }.width('20%')
470  }
471
472  build() {
473    Column() {
474      Tabs({ barPosition: BarPosition.Start, controller: this.tabsController }) {
475        ForEach(this.list, (index: number) =>{
476          TabContent().tabBar(this.tabBuilder(index, '页签 ' + this.list[index]))
477        })
478      }
479      .onTabBarClick((index: number) => {
480        this.currentIndex = index;
481        this.swiperController.changeIndex(index, true);
482      })
483      .barMode(BarMode.Scrollable)
484      .backgroundColor('#F1F3F5')
485      .height(56)
486      .width('100%')
487
488      Swiper(this.swiperController) {
489        LazyForEach(this.swiperData, (item: string) => {
490          Text(item.toString())
491            .onAppear(()=>{
492              console.info('onAppear ' + item.toString());
493            })
494            .onDisAppear(()=>{
495              console.info('onDisAppear ' + item.toString());
496            })
497            .width('100%')
498            .height('40%')
499            .backgroundColor(0xAFEEEE)
500            .textAlign(TextAlign.Center)
501            .fontSize(30)
502        }, (item: string) => item)
503      }
504      .loop(false)
505      .onSelected((index: number) => {
506        console.info("onSelected:" + index);
507        this.currentIndex = index;
508        this.tabsController.changeIndex(index);
509      })
510    }
511  }
512}
513```
514![Swiper与Tabs联动](figures/tabs_swiper.gif)
515
516## 设置圆点导航点间距
517
518针对圆点导航点,可以通过DotIndicator的space属性来设置圆点导航点的间距。
519
520```ts
521Swiper() {
522  // ...
523}
524.indicator(
525  new DotIndicator()
526    .space(LengthMetrics.vp(3))
527)
528```
529
530## 导航点忽略组件大小
531
532当导航点的bottom设为0之后,导航点的底部与Swiper的底部还会有一定间距。如果希望消除该间距,可通过调用bottom(bottom, ignoreSize)属性来进行设置。将ignoreSize 设置为true,即可忽略导航点组件大小,达到消除该间距的目的。
533
534- 圆点导航点忽略组件大小。
535
536```ts
537Swiper() {
538  // ...
539}
540.indicator(
541  new DotIndicator()
542    .bottom(LengthMetrics.vp(0), true)
543)
544```
545
546- 数字导航点忽略组件大小。
547
548```ts
549Swiper() {
550  // ...
551}
552.indicator(
553  new DigitIndicator()
554    .bottom(LengthMetrics.vp(0), true)
555)
556```
557
558圆点导航点设置间距及忽略组件大小完整示例代码如下:
559
560```ts
561import { LengthMetrics } from '@kit.ArkUI';
562
563// MyDataSource.ets
564class MyDataSource implements IDataSource {
565  private list: number[] = [];
566
567  constructor(list: number[]) {
568    this.list = list;
569  }
570
571  totalCount(): number {
572    return this.list.length;
573  }
574
575  getData(index: number): number {
576    return this.list[index];
577  }
578
579  registerDataChangeListener(listener: DataChangeListener): void {
580  }
581
582  unregisterDataChangeListener() {
583  }
584}
585
586// SwiperExample.ets
587@Entry
588@Component
589struct SwiperExample {
590
591  @State space: LengthMetrics = LengthMetrics.vp(0);
592  @State spacePool: LengthMetrics[] = [LengthMetrics.vp(0), LengthMetrics.px(3), LengthMetrics.vp(10)];
593  @State spaceIndex: number = 0;
594
595  @State ignoreSize: boolean = false;
596  @State ignoreSizePool: boolean[] = [false, true];
597  @State ignoreSizeIndex: number = 0;
598
599  private swiperController1: SwiperController = new SwiperController();
600  private data1: MyDataSource = new MyDataSource([]);
601
602  aboutToAppear(): void {
603    let list1: number[] = [];
604    for (let i = 1; i <= 10; i++) {
605      list1.push(i);
606    }
607    this.data1 = new MyDataSource(list1);
608  }
609
610  build() {
611    Scroll() {
612      Column({ space: 20 }) {
613        Swiper(this.swiperController1) {
614          LazyForEach(this.data1, (item: string) => {
615            Text(item.toString())
616              .width('90%')
617              .height(120)
618              .backgroundColor(0xAFEEEE)
619              .textAlign(TextAlign.Center)
620              .fontSize(30)
621          }, (item: string) => item)
622        }
623        .indicator(new DotIndicator()
624          .space(this.space)
625          .bottom(LengthMetrics.vp(0), this.ignoreSize)
626          .itemWidth(15)
627          .itemHeight(15)
628          .selectedItemWidth(15)
629          .selectedItemHeight(15)
630          .color(Color.Gray)
631          .selectedColor(Color.Blue))
632        .displayArrow({
633          showBackground: true,
634          isSidebarMiddle: true,
635          backgroundSize: 24,
636          backgroundColor: Color.White,
637          arrowSize: 18,
638          arrowColor: Color.Blue
639        }, false)
640
641        Column({ space: 4 }) {
642          Button('spaceIndex:' + this.spaceIndex).onClick(() => {
643            this.spaceIndex = (this.spaceIndex + 1) % this.spacePool.length;
644            this.space = this.spacePool[this.spaceIndex];
645          }).margin(10)
646
647          Button('ignoreSizeIndex:' + this.ignoreSizeIndex).onClick(() => {
648            this.ignoreSizeIndex = (this.ignoreSizeIndex + 1) % this.ignoreSizePool.length;
649            this.ignoreSize = this.ignoreSizePool[this.ignoreSizeIndex];
650          }).margin(10)
651        }.margin(2)
652      }.width('100%')
653    }
654  }
655}
656```
657
658![controll](figures/indicator_space.gif)
659
660## 保持可见内容位置不变
661
662Swiper通过设置[maintainVisibleContentPosition](../reference/apis-arkui/arkui-ts/ts-container-swiper.md#maintainvisiblecontentposition20)属性,可在使用LazyForEach懒加载数据时(如通过onDataAdd新增数据),保持当前可见内容位置不变,避免因数据增删导致的视图跳动。该属性默认值为false。
663
664maintainVisibleContentPosition为true时,显示区域上方或前方插入或删除数据时可见内容位置不变。
665
666关于数据[LazyForEach:懒加载](../ui/state-management/arkts-rendering-control-lazyforeach.md)的具体使用,可参考数据懒加载章节中的示例。
667
668```ts
669// xxx.ets
670class MyDataSource implements IDataSource {
671  private listeners: DataChangeListener[] = [];
672  private dataArray: string[] = ['0', '1', '2', '3', '4', '5', '6'];
673
674  public totalCount(): number {
675    return this.dataArray.length;
676  }
677
678  public getData(index: number): string | undefined {
679    return this.dataArray[index];
680  }
681
682  public addData(index: number, data: string): void {
683    this.dataArray.splice(index, 0, data);
684    this.listeners.forEach(listener => {
685      listener.onDataAdd(index);
686    })
687  }
688
689  public deleteData(index: number): void {
690    this.dataArray.splice(index, 1);
691    this.listeners.forEach(listener => {
692      listener.onDataDelete(index);
693    })
694  }
695
696  registerDataChangeListener(listener: DataChangeListener): void {
697    if (this.listeners.indexOf(listener) < 0) {
698      console.info('add listener');
699      this.listeners.push(listener);
700    }
701  }
702
703  unregisterDataChangeListener(listener: DataChangeListener): void {
704    const pos = this.listeners.indexOf(listener);
705    if (pos >= 0) {
706      console.info('remove listener');
707      this.listeners.splice(pos, 1);
708    }
709  }
710}
711
712@Entry
713@Component
714struct SwiperExample {
715  private data: MyDataSource = new MyDataSource();
716  @State index: number = 3;
717  build() {
718    Column({ space: 5 }) {
719      Swiper() {
720        LazyForEach(this.data, (item: string) => {
721          Text(item.toString())
722            .width('90%')
723            .height(160)
724            .backgroundColor(0xAFEEEE)
725            .textAlign(TextAlign.Center)
726            .fontSize(30)
727        })
728      }
729      .onChange((index) => {
730        this.index = index;
731      })
732      .index(3)
733      .maintainVisibleContentPosition(true)
734
735      Column({ space: 12 }) {
736        Text("index:" + this.index).fontSize(20)
737        Row() {
738          // 在LazyForEach索引为0的位置添加数据
739          Button('header data add').height(30).onClick(() => {
740            this.data.addData(0, 'header Data');
741          })
742          // 删除LazyForEach索引为0的位置数据
743          Button('header data delete').height(30).onClick(() => {
744            this.data.deleteData(0);
745          })
746        }
747      }.margin(5)
748    }.width('100%')
749    .margin({ top: 5 })
750  }
751}
752```
753
754![controll](figures/maintainVisibleContentPosition_true.gif)
755
756## 相关实例
757
758针对Swiper组件开发,有以下相关实例可供参考:
759
760- [电子相册(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/ETSUI/ElectronicAlbum)
761
762- [Swiper的使用(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/ETSUI/SwiperArkTS)
763<!--RP1--><!--RP1End-->