grid组件及数据懒加载

在常用的手机应用中,经常会见到一些数据列表,如设置页面、通讯录、商品列表等。下图中两个页面都包含列表,“首页”页面中包含两个网格布局,“商城”页面中包含一个商品列表。

grid组件及数据懒加载-鸿蒙开发者社区

透过上面的现象,其实可以把以上抽象为两类列表,一个是网格列表(Grid),一个是线性列表(List),具体抽象出来的图像如下图。

grid组件及数据懒加载-鸿蒙开发者社区

HarmonyOS
2024-05-26 16:25:49
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
lovingkane

使用的核心API

Grid-容器组件

LazyForEach:数据懒加载

核心代码解释

和List组件一样,Grid组件也可以使用ForEach来渲染多个列表项GridItem,示例代码中创建了16个GridItem列表项。同时设置columnsTemplate的值为'1fr 1fr 1fr 1fr',表示这个网格为4列,将Grid允许的宽分为4等分,每列占1份;rowsTemplate的值为'1fr 1fr 1fr 1fr',表示这个网格为4行,将Grid允许的高分为4等分,每行占1份。

这样就构成了一个4行4列的网格列表,然后使用columnsGap设置列间距为10vp,使用rowsGap设置行间距也为10vp。

@Entry 
@Component 
struct GridExample { 
  // 定义一个长度为16的数组 
  /** 
   * 经查阅资料,此处参数意思详细为map((item,index,arr) => `item ${index}`); 
 
   * item代表操作数组中的项 
 
   * index代表操作数组中的项的索引 
 
   * arr代表要操作的数组 
 
   * 具体参数名任意,所以此处的下划线_是一个标识符,代表数组中的项 
   */ 
  private arr: string[] = new Array(16).fill('').map((item: string, index,) => `item ${index}`); 
 
  build() { 
    Column() { 
      Grid() { 
        ForEach(this.arr, (item: string) => { 
          GridItem() { 
            Text(item) 
              .fontSize(16) 
              .fontColor(Color.White) 
              .backgroundColor(0x007DFF) 
              .width('100%') 
              .height('100%') 
              .textAlign(TextAlign.Center) 
          } 
        }, (item: string) => item) 
      } 
      .columnsTemplate('1fr 1fr 1fr 1fr') 
      .rowsTemplate('1fr 1fr 1fr 1fr') 
      .columnsGap(10) 
      .rowsGap(10) 
      .height(300) 
    } 
    .width('100%') 
    .padding(12) 
    .backgroundColor(0xF1F3F5) 
  } 
}

上面构建的网格布局使用了固定的行数和列数,所以构建出的网格是不可滚动的。然而有时候因为内容较多,我们通过滚动的方式来显示更多的内容,就需要一个可以滚动的网格布局。我们只需要设置rowsTemplate和columnsTemplate中的一个即可。

将示例代码中GridItem的高度设置为固定值,例如100;仅设置columnsTemplate属性,不设置rowsTemplate属性,就可以实现Grid列表的滚动。

@Entry 
@Component 
struct GridExample { 
  //仅设置columnsTemplate属性,不设置rowsTemplate属性,就可以实现Grid列表的滚动 
  private arr: string[] = new Array(16).fill('').map((item: string, index,) => `item ${index}`); 
 
  build() { 
    Column() { 
      Grid() { 
        ForEach(this.arr, (item: string) => { 
          GridItem() { 
            Text(item) 
              .fontSize(16) 
              .fontColor(Color.White) 
              .backgroundColor(0x007DFF) 
              .width('100%') 
              .height(100) 
              .textAlign(TextAlign.Center) 
          } 
        }, (item: string) => item) 
      } 
      .columnsTemplate('1fr 1fr 1fr 1fr') 
 
      .columnsGap(10) 
      .rowsGap(10) 
      .height(300) 
    } 
    .width('100%') 
    .padding(12) 
    .backgroundColor(0xF1F3F5) 
  } 
}

基于以上示例都是在理想情况下演示,一般list和gird是日常开发中用的最多的容器组件,一般在实际开发中我们数据库的内容成千上万,假如我们要一次性全部在手机上记载出来,那是不可能的,所以为了用户的体验,我们一般会用数据懒加载来实现这种多数据展示,而对用户来说是无感知的,增强用户体验。

LazyForEach从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach,框架会根据滚动容器可视区域按需创建组件,当组件滑出可视区域外时,框架会进行组件销毁回收以降低内存占用。

代码如下:

// Basic implementation of IDataSource to handle data listener 
// IDataSource处理数据监听器的基本实现 
class BasicDataSource implements IDataSource { 
  private listeners: DataChangeListener[] = []; // 数据监听器 
  private originDataArray: string[] = []; 
 
  //获得数据总数 
  public totalCount(): number { 
    return 0; 
  } 
 
  // 获取索引值对应的数据 
  public getData(index: number): string { 
    return this.originDataArray[index]; 
  } 
 
  // 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听 
  registerDataChangeListener(listener: DataChangeListener): void { 
    if (this.listeners.indexOf(listener) < 0) { // 返回某个值在数组中第一次出现的索引,如果不存在则返回-1, 
      console.info('add listener'); 
      this.listeners.push(listener); // 将新元素追加到数组的末尾 
    } 
  } 
 
  // 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听 
  unregisterDataChangeListener(listener: DataChangeListener): void { 
    const pos = this.listeners.indexOf(listener); 
    if (pos >= 0) { 
      console.info('remove listener'); 
      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); 
    }) 
  } 
} 
 
class MyDataSource extends BasicDataSource { 
  private dataArray: string[] = []; 
 
  public totalCount(): number { 
    return this.dataArray.length; 
  } 
 
  public getData(index: number): string { 
    return this.dataArray[index]; 
  } 
 
  public addData(index: number, data: string): void { 
    this.dataArray.splice(index, 0, data); // 参数一:删除元素的位置,参数二:删除元素的个数,参数三:要插入数组中的元素 
    this.notifyDataAdd(index); 
  } 
 
  public pushData(data: string): void { 
    this.dataArray.push(data); 
    this.notifyDataAdd(this.dataArray.length - 1); 
  } 
} 
 
@Entry 
@Component 
struct MyComponent { 
  private arr: string[] = new Array(100).fill('').map((item: string, index,) => `item ${index}`); 
  private data: MyDataSource = new MyDataSource(); 
 
  aboutToAppear() { 
    for (let i = 0; i < this.arr.length; i++) { 
      this.data.pushData(this.arr[i]); 
    } 
    // for (let i = 0; i <= 20; i++) { 
    //   this.data.pushData(`Hello ${i}`) 
    // } 
  } 
 
  build() { 
    Column() { 
      Grid() { 
        LazyForEach(this.data, (item: string) => { 
          GridItem() { 
            Text(item) 
              .fontSize(16) 
              .fontColor(Color.White) 
              .backgroundColor(0x007DFF) 
              .width('100%') 
              .height(100) 
              .textAlign(TextAlign.Center) 
              .onAppear(()=>{ 
                console.info('appear:'+item) 
              }) 
          } 
        }, (item: string) => item) 
      } 
      .cachedCount(5)   //先加载10个 
      .columnsTemplate('1fr 1fr 1fr 1fr') 
      .columnsGap(10) 
      .rowsGap(10) 
      .height(300) 
    } 
    .width('100%') 
    .padding(12) 
    .backgroundColor(Color.White) 
  } 
}

实现效果

分享
微博
QQ
微信
回复
2024-05-27 21:52:24
相关问题
Tabs组件加载的问题
750浏览 • 1回复 待解决
如何实现Fraction加载功能?
6050浏览 • 1回复 待解决
使用LazyForEach加载列表相关问题
326浏览 • 1回复 待解决
LazyForEach加载的原理是什么
672浏览 • 1回复 待解决
界面内容瀑布流加载实现
452浏览 • 1回复 待解决
数组嵌套数组场景的加载实现
226浏览 • 1回复 待解决
使用Grid 组件实现选座场景
338浏览 • 1回复 待解决
Grid组件如何实现高度自适应
1363浏览 • 1回复 待解决
ListListItem组件的使用
456浏览 • 1回复 待解决
Grid组件的scrollBar是否支持自定义
812浏览 • 1回复 待解决
Text 组件如何加载Unicode字符
505浏览 • 1回复 待解决
TabContent组件是否支持预加载
394浏览 • 1回复 待解决
多hap调用数据传递,有人知道吗?
489浏览 • 1回复 待解决
如何禁止web组件自动加载图片?
374浏览 • 1回复 待解决
grid如何怎么实现拖拽功能
334浏览 • 1回复 待解决
web组件对html文件的加载
387浏览 • 1回复 待解决
Image组件如何加载网络图片
904浏览 • 1回复 待解决
ListContainer加载大量数据白屏并且卡UI
3839浏览 • 1回复 待解决