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实现该接口,提供具备预取能力的DataSource
137
138**元服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
139
140**系统能力:** SystemCapability.ArkUI.ArkUI.Full
141
142### prefetch
143**元服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
144
145**系统能力:** SystemCapability.ArkUI.ArkUI.Full
146
147**参数说明:**
148
149| 参数名   | 类型     | 必填 | 说明       |
150|-------|--------|----|----------|
151| index | number | 是  | 预取数据项索引值 |
152
153### cancel
154**元服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
155
156**系统能力:** SystemCapability.ArkUI.ArkUI.Full
157
158**参数说明:**
159
160| 参数名   | 类型     | 必填 | 说明         |
161|-------|--------|----|------------|
162| index | number | 是  | 取消预取数据项索引值 |
163
164列表内容移出屏幕时(比如列表快速滑动场景下),预取算法判断屏幕以外的Item可以被取消预取时,该方法即会被调用。例如,如果HTTP框架支持请求取消,则可以在此处取消在prefetch中发起的网络请求。
165
166## 示例
167
168```typescript
169import { BasicPrefetcher, IDataSourcePrefetching } from '@kit.ArkUI';
170import { image } from '@kit.ImageKit';
171
172const ITEMS_ON_SCREEN = 8;
173
174@Entry
175@Component
176struct PrefetcherDemoComponent {
177  private readonly dataSource = new MyDataSource(2000, 500);
178  private readonly prefetcher = new BasicPrefetcher(this.dataSource);
179
180  build() {
181    Column() {
182      List() {
183        LazyForEach(this.dataSource, (item: PictureItem) => {
184          ListItem() {
185            PictureItemComponent({ info: item })
186              .height(`${100 / ITEMS_ON_SCREEN}%`)
187          }
188        }, (item: PictureItem) => item.title)
189      }
190      .onScrollIndex((start: number, end: number) => {
191        this.prefetcher.visibleAreaChanged(start, end);
192      })
193    }
194  }
195}
196
197@Component
198struct PictureItemComponent {
199  @ObjectLink info: PictureItem;
200
201  build() {
202    Row() {
203      Image(this.info.imagePixelMap)
204        .objectFit(ImageFit.Contain)
205        .width('40%')
206      Text(this.info.title)
207        .width('60%')
208    }
209  }
210}
211
212@Observed
213class PictureItem {
214  readonly color: number;
215  title: string;
216  imagePixelMap: image.PixelMap | undefined;
217  key: string;
218
219  constructor(color: number, title: string) {
220    this.color = color;
221    this.title = title;
222    this.key = title;
223  }
224}
225
226type ItemIndex = number;
227type TimerId = number;
228
229class MyDataSource implements IDataSourcePrefetching {
230  private readonly items: PictureItem[];
231  private readonly fetchDelayMs: number;
232  private readonly fetches: Map<ItemIndex, TimerId> = new Map();
233
234  constructor(numItems: number, fetchDelayMs: number) {
235    this.items = [];
236    this.fetchDelayMs = fetchDelayMs;
237    for (let i = 0; i < numItems; i++) {
238      const item = new PictureItem(getRandomColor(), `Item ${i}`)
239      this.items.push(item);
240    }
241  }
242
243  async prefetch(index: number): Promise<void> {
244    const item = this.items[index];
245    if (item.imagePixelMap) {
246      return;
247    }
248
249    // 模拟高耗时操作
250    return new Promise<void>(resolve => {
251      const timeoutId = setTimeout(async () => {
252        this.fetches.delete(index);
253        const bitmap = create10x10Bitmap(item.color);
254        const imageSource: image.ImageSource = image.createImageSource(bitmap);
255        item.imagePixelMap = await imageSource.createPixelMap();
256        resolve();
257      }, this.fetchDelayMs);
258
259      this.fetches.set(index, timeoutId)
260    });
261  }
262
263  cancel(index: number): void {
264    const timerId = this.fetches.get(index);
265    if (timerId) {
266      this.fetches.delete(index);
267      clearTimeout(timerId);
268    }
269  }
270
271  totalCount(): number {
272    return this.items.length;
273  }
274
275  getData(index: number): PictureItem {
276    return this.items[index];
277  }
278
279  registerDataChangeListener(_: DataChangeListener): void {
280  }
281
282  unregisterDataChangeListener(_: DataChangeListener): void {
283  }
284}
285
286function getRandomColor(): number {
287  const maxColorCode = 256;
288  const r = Math.floor(Math.random() * maxColorCode);
289  const g = Math.floor(Math.random() * maxColorCode);
290  const b = Math.floor(Math.random() * maxColorCode);
291
292  return (r * 256 + g) * 256 + b;
293}
294
295function create10x10Bitmap(color: number): ArrayBuffer {
296  const height = 10;
297  const width = 10;
298
299  const fileHeaderLength = 14;
300  const bitmapInfoLength = 40;
301  const headerLength = fileHeaderLength + bitmapInfoLength;
302  const pixelSize = (width * 3 + 2) * height;
303
304  let length = pixelSize + headerLength;
305
306  const buffer = new ArrayBuffer(length);
307  const view16 = new Uint16Array(buffer);
308
309  view16[0] = 0x4D42;
310  view16[1] = length & 0xffff;
311  view16[2] = length >> 16;
312  view16[5] = headerLength;
313
314  let offset = 7;
315  view16[offset++] = bitmapInfoLength & 0xffff;
316  view16[offset++] = bitmapInfoLength >> 16;
317  view16[offset++] = width & 0xffff;
318  view16[offset++] = width >> 16;
319  view16[offset++] = height & 0xffff;
320  view16[offset++] = height >> 16;
321  view16[offset++] = 1;
322  view16[offset++] = 24;
323
324  const b = color & 0xff;
325  const g = (color >> 8) & 0xff;
326  const r = color >> 16;
327  offset = headerLength;
328  const view8 = new Uint8Array(buffer);
329  for (let y = 0; y < height; y++) {
330    for (let x = 0; x < width; x++) {
331      view8[offset++] = b;
332      view8[offset++] = g;
333      view8[offset++] = r;
334    }
335    offset += 2;
336  }
337
338  return buffer;
339}
340```