1# @ohos.arkui.Prefetcher (Prefetching)
2配合LazyForEach,为List、Grid、Waterfall和Swiper等容器组件滑动浏览时提供内容预加载能力,提升用户浏览体验。
3
4>  **说明:**
5>
6>  本模块首批接口从API version 12开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
7
8## 导入模块
9
10```ts
11import { BasicPrefetcher, IDataSourcePrefetching, IPrefetcher } from '@kit.ArkUI';
12```
13
14
15## IPrefetcher
16实现此接口以提供预取能力。
17
18**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
19
20**系统能力:** SystemCapability.ArkUI.ArkUI.Full
21
22### setDataSource
23setDataSource(dataSource: IDataSourcePrefetching): void;
24
25设置支持预取的DataSource以绑定到Prefetcher
26
27**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
28
29**系统能力:** SystemCapability.ArkUI.ArkUI.Full
30
31**参数:**
32
33| 参数名        | 类型                                                | 必填 | 说明         |
34|------------|---------------------------------------------------|----|------------|
35| dataSource | [IDataSourcePrefetching](#idatasourceprefetching) | 是  | 支持预取能力的数据源 |
36
37```typescript
38class MyPrefetcher implements IPrefetcher {
39  private dataSource?: IDataSourcePrefetching;
40
41  setDataSource(dataSource: IDataSourcePrefetching): void {
42    this.dataSource = dataSource;
43  }
44
45  visibleAreaChanged(minVisible: number, maxVisible: number): void {
46    this.dataSource?.prefetch(minVisible);
47  }
48}
49```
50
51### visibleAreaChanged
52visibleAreaChanged(minVisible: number, maxVisible: number): void;
53
54当可见区域边界发生改变时调用此方法。支持与`List`、`Grid`、`Waterfall`和`Swiper`组件配合使用
55
56**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
57
58**系统能力:** SystemCapability.ArkUI.ArkUI.Full
59
60**参数:**
61
62| 参数名        | 类型     | 必填 | 说明        |
63|------------|--------|----|-----------|
64| minVisible | number | 是  | 列表可见区域的上界 |
65| maxVisible | number | 是  | 列表可见区域的下界 |
66
67```typescript
68class MyPrefetcher implements IPrefetcher {
69  private dataSource?: IDataSourcePrefetching;
70
71  setDataSource(dataSource: IDataSourcePrefetching): void {
72    this.dataSource = dataSource;
73  }
74
75  visibleAreaChanged(minVisible: number, maxVisible: number): void {
76    this.dataSource?.prefetch(minVisible);
77  }
78}
79```
80
81## BasicPrefetcher
82BasicPrefetcher是IPrefetcher的基础实现。它提供了一种智能数据预取算法,以根据屏幕上可见区域的实时变化和预取持续时间的变化来决定应预取哪些数据项。它还可以根据用户的滚动操作来确定哪些预取请求应该被取消。
83
84**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
85
86**系统能力:** SystemCapability.ArkUI.ArkUI.Full
87
88### constructor
89constructor(dataSource?: IDataSourcePrefetching);
90
91传入支持预取的DataSource以绑定到Prefetcher
92
93**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
94
95**系统能力:** SystemCapability.ArkUI.ArkUI.Full
96
97**参数:**
98
99| 参数名        | 类型                                                | 必填 | 说明         |
100|------------|---------------------------------------------------|----|------------|
101| dataSource | [IDataSourcePrefetching](#idatasourceprefetching) | 否  | 支持预取能力的数据源 |
102
103### setDataSource
104setDataSource(dataSource: IDataSourcePrefetching): void;
105
106设置支持预取的DataSource以绑定到Prefetcher
107
108**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
109
110**系统能力:** SystemCapability.ArkUI.ArkUI.Full
111
112**参数:**
113
114| 参数名        | 类型                                                | 必填 | 说明         |
115|------------|---------------------------------------------------|----|------------|
116| dataSource | [IDataSourcePrefetching](#idatasourceprefetching) | 是  | 支持预取能力的数据源 |
117
118### visibleAreaChanged
119visibleAreaChanged(minVisible: number, maxVisible: number): void;
120
121当可见区域边界发生改变时调用此方法。支持与`List`、`Grid`、`Waterfall`和`Swiper`组件配合使用
122
123**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
124
125**系统能力:** SystemCapability.ArkUI.ArkUI.Full
126
127**参数:**
128
129| 参数名        | 类型     | 必填 | 说明        |
130|------------|--------|----|-----------|
131| minVisible | number | 是  | 列表可见区域的上界 |
132| maxVisible | number | 是  | 列表可见区域的下界 |
133
134## IDataSourcePrefetching
135
136继承自[IDataSource](./arkui-ts/ts-rendering-control-lazyforeach.md#idatasource10)。实现该接口,提供具备预取能力的DataSource。
137
138**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
139
140**系统能力:** SystemCapability.ArkUI.ArkUI.Full
141
142### prefetch
143prefetch(index: number): Promise\<void\> | void;
144
145从数据集中预取指定的元素。该方法可以为同步,也可为异步。
146
147**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
148
149**系统能力:** SystemCapability.ArkUI.ArkUI.Full
150
151**参数:**
152
153| 参数名   | 类型     | 必填 | 说明       |
154|-------|--------|----|----------|
155| index | number | 是  | 预取数据项索引值 |
156
157### cancel
158cancel?(index: number): Promise\<void\> | void;
159
160取消从数据集中预取指定的元素。该方法可以为同步,也可为异步。
161
162**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
163
164**系统能力:** SystemCapability.ArkUI.ArkUI.Full
165
166**参数:**
167
168| 参数名   | 类型     | 必填 | 说明         |
169|-------|--------|----|------------|
170| index | number | 是  | 取消预取数据项索引值 |
171
172列表内容移出屏幕时(比如列表快速滑动场景下),预取算法判断屏幕以外的Item可以被取消预取时,该方法即会被调用。例如,如果HTTP框架支持请求取消,则可以在此处取消在prefetch中发起的网络请求。
173
174## 示例
175
176下面示例展示了Prefetcher配合LazyForEach实现的懒加载效果。
177
178```typescript
179import { BasicPrefetcher, IDataSourcePrefetching } from '@kit.ArkUI';
180import { image } from '@kit.ImageKit';
181
182const ITEMS_ON_SCREEN = 8;
183
184@Entry
185@Component
186struct PrefetcherDemoComponent {
187  private readonly dataSource = new MyDataSource(2000, 500);
188  private readonly prefetcher = new BasicPrefetcher(this.dataSource);
189
190  build() {
191    Column() {
192      List() {
193        LazyForEach(this.dataSource, (item: PictureItem) => {
194          ListItem() {
195            PictureItemComponent({ info: item })
196              .height(`${100 / ITEMS_ON_SCREEN}%`)
197          }
198        }, (item: PictureItem) => item.title)
199      }
200      .onScrollIndex((start: number, end: number) => {
201        this.prefetcher.visibleAreaChanged(start, end);
202      })
203    }
204  }
205}
206
207@Component
208struct PictureItemComponent {
209  @ObjectLink info: PictureItem;
210
211  build() {
212    Row() {
213      Image(this.info.imagePixelMap)
214        .objectFit(ImageFit.Contain)
215        .width('40%')
216      Text(this.info.title)
217        .width('60%')
218    }
219  }
220}
221
222@Observed
223class PictureItem {
224  readonly color: number;
225  title: string;
226  imagePixelMap: image.PixelMap | undefined;
227  key: string;
228
229  constructor(color: number, title: string) {
230    this.color = color;
231    this.title = title;
232    this.key = title;
233  }
234}
235
236type ItemIndex = number;
237type TimerId = number;
238
239class MyDataSource implements IDataSourcePrefetching {
240  private readonly items: PictureItem[];
241  private readonly fetchDelayMs: number;
242  private readonly fetches: Map<ItemIndex, TimerId> = new Map();
243
244  constructor(numItems: number, fetchDelayMs: number) {
245    this.items = [];
246    this.fetchDelayMs = fetchDelayMs;
247    for (let i = 0; i < numItems; i++) {
248      const item = new PictureItem(getRandomColor(), `Item ${i}`)
249      this.items.push(item);
250    }
251  }
252
253  async prefetch(index: number): Promise<void> {
254    const item = this.items[index];
255    if (item.imagePixelMap) {
256      return;
257    }
258
259    // 模拟高耗时操作
260    return new Promise<void>(resolve => {
261      const timeoutId = setTimeout(async () => {
262        this.fetches.delete(index);
263        const bitmap = create10x10Bitmap(item.color);
264        const imageSource: image.ImageSource = image.createImageSource(bitmap);
265        item.imagePixelMap = await imageSource.createPixelMap();
266        resolve();
267      }, this.fetchDelayMs);
268
269      this.fetches.set(index, timeoutId)
270    });
271  }
272
273  cancel(index: number): void {
274    const timerId = this.fetches.get(index);
275    if (timerId) {
276      this.fetches.delete(index);
277      clearTimeout(timerId);
278    }
279  }
280
281  totalCount(): number {
282    return this.items.length;
283  }
284
285  getData(index: number): PictureItem {
286    return this.items[index];
287  }
288
289  registerDataChangeListener(_: DataChangeListener): void {
290  }
291
292  unregisterDataChangeListener(_: DataChangeListener): void {
293  }
294}
295
296function getRandomColor(): number {
297  const maxColorCode = 256;
298  const r = Math.floor(Math.random() * maxColorCode);
299  const g = Math.floor(Math.random() * maxColorCode);
300  const b = Math.floor(Math.random() * maxColorCode);
301
302  return (r * 256 + g) * 256 + b;
303}
304
305function create10x10Bitmap(color: number): ArrayBuffer {
306  const height = 10;
307  const width = 10;
308
309  const fileHeaderLength = 14;
310  const bitmapInfoLength = 40;
311  const headerLength = fileHeaderLength + bitmapInfoLength;
312  const pixelSize = (width * 3 + 2) * height;
313
314  let length = pixelSize + headerLength;
315
316  const buffer = new ArrayBuffer(length);
317  const view16 = new Uint16Array(buffer);
318
319  view16[0] = 0x4D42;
320  view16[1] = length & 0xffff;
321  view16[2] = length >> 16;
322  view16[5] = headerLength;
323
324  let offset = 7;
325  view16[offset++] = bitmapInfoLength & 0xffff;
326  view16[offset++] = bitmapInfoLength >> 16;
327  view16[offset++] = width & 0xffff;
328  view16[offset++] = width >> 16;
329  view16[offset++] = height & 0xffff;
330  view16[offset++] = height >> 16;
331  view16[offset++] = 1;
332  view16[offset++] = 24;
333
334  const b = color & 0xff;
335  const g = (color >> 8) & 0xff;
336  const r = color >> 16;
337  offset = headerLength;
338  const view8 = new Uint8Array(buffer);
339  for (let y = 0; y < height; y++) {
340    for (let x = 0; x < width; x++) {
341      view8[offset++] = b;
342      view8[offset++] = g;
343      view8[offset++] = r;
344    }
345    offset += 2;
346  }
347
348  return buffer;
349}
350```