Skip to content

基础列表示例

本示例展示如何创建一个简单的垂直滚动列表,包含 1000 条数据,演示虚拟列表的高性能渲染能力。

完整示例代码

ts
import { _decorator, Component, Label, Node, instantiate, Prefab, Color } from 'cc';
import { VirtualViewList } from 'assets/Script/Core/VirtualList/VirtualViewList';

const { ccclass, property } = _decorator;

@ccclass('BasicListExample')
export class BasicListExample extends Component {
  @property(Prefab)
  itemPrefab: Prefab | null = null;

  private data: { id: number; text: string; color: Color }[] = [];

  onLoad() {
    // 生成 1000 条测试数据
    this.data = Array.from({ length: 1000 }, (_, i) => ({
      id: i,
      text: `Item ${i + 1}`,
      color: this.getRandomColor()
    }));
  }

  start() {
    const list = this.node.getComponent(VirtualViewList)!;

    // 1. 预加载节点优化首屏性能
    list.PreloadItems(10);

    // 2. 注册模板
    list.RegisterTemplate('default', () => instantiate(this.itemPrefab!), true);

    // 3. 设置回调
    list.SetCallbacks({
      onItemInit: (node, index) => this.initItem(node, index),
      onItemUpdate: (node, index) => this.updateItem(node, index),
      onLoadFinished: () => {
        console.log('列表加载完成');
        this.showToast('加载完成');
      },
      onScrolling: (ratio) => {
        // 显示滚动进度
        console.log(`滚动进度: ${(ratio * 100).toFixed(1)}%`);
      }
    });

    // 4. 加载数据
    const types = this.data.map(() => 'default');
    list.ReloadData(types);
  }

  // 首次创建节点时调用(一次性初始化)
  private initItem(node: Node, index: number) {
    // 绑定点击事件
    node.on(Node.EventType.TOUCH_END, () => {
      this.onItemClick(index);
    });
  }

  // 节点重新显示时调用(更新数据)
  private updateItem(node: Node, index: number) {
    const itemData = this.data[index];
    
    // 更新文本
    const label = node.getChildByName('Label')?.getComponent(Label);
    if (label) {
      label.string = itemData.text;
      label.color = itemData.color;
    }

    // 更新序号
    const indexLabel = node.getChildByName('Index')?.getComponent(Label);
    if (indexLabel) {
      indexLabel.string = `#${itemData.id}`;
    }
  }

  // 点击事件处理
  private onItemClick(index: number) {
    console.log(`点击了 Item ${index}:`, this.data[index]);
    this.showToast(`点击了: ${this.data[index].text}`);
  }

  // 随机颜色生成
  private getRandomColor(): Color {
    const colors = [
      Color.RED,
      Color.GREEN,
      Color.BLUE,
      Color.YELLOW,
      Color.CYAN,
      Color.MAGENTA
    ];
    return colors[Math.floor(Math.random() * colors.length)];
  }

  // Toast 提示(简化实现)
  private showToast(message: string) {
    // 这里可以接入你的 Toast 组件
    console.log('[Toast]', message);
  }
}

场景搭建步骤

1. 创建 ScrollView

  1. 在 Hierarchy 中右键 → 创建 → UI → ScrollView
  2. 选择 ScrollView 节点,设置:
    • Content Size Hint: 勾选
    • Horizontal: 取消勾选(仅垂直滚动)
    • Vertical: 勾选

2. 挂载 VirtualViewList

  1. 选择 ScrollView 的 content 子节点
  2. 添加组件 → 添加用户脚本组件 → VirtualViewList
  3. 在属性面板中:
    • ScrollView: 拖入父节点的 ScrollView 组件
    • Layout Type: 选择 VERTICAL
    • Item Spacing: 设置为 10
    • Padding Top/Bottom: 根据需要设置

3. 创建列表项预制体

  1. 创建一个节点命名为 ItemPrefab
  2. 添加子节点:
    • Background:添加 Sprite 组件(背景)
    • Label:添加 Label 组件(主文本)
    • Index:添加 Label 组件(序号)
  3. 调整布局和样式
  4. ItemPrefab 拖入 assets 文件夹制作成预制体

4. 挂载脚本

  1. 选择 content 节点
  2. 添加组件 → 添加用户脚本组件 → BasicListExample
  3. 在属性面板中:
    • Item Prefab: 拖入刚才创建的预制体

5. 运行场景

点击运行按钮,即可看到包含 1000 条数据的流畅滚动列表。

功能扩展示例

添加搜索过滤功能

ts
private filterData(keyword: string) {
  const filtered = this.data.filter(item => 
    item.text.toLowerCase().includes(keyword.toLowerCase())
  );
  
  const types = filtered.map(() => 'default');
  list.ReloadData(types);
}

添加下拉刷新

ts
list.SetCallbacks({
  // ... 其他回调
  onPullDownRefresh: async () => {
    // 模拟请求新数据
    await this.sleep(1000);
    
    const newData = Array.from({ length: 10 }, (_, i) => ({
      id: Date.now() + i,
      text: `新数据 ${i + 1}`,
      color: this.getRandomColor()
    }));
    
    // 添加到数据头部
    this.data.unshift(...newData);
    
    return {
      types: newData.map(() => 'default')
    };
  }
});

添加上拉加载更多

ts
private hasMore = true;
private currentPage = 1;

list.SetCallbacks({
  // ... 其他回调
  onPullUpLoad: async () => {
    if (!this.hasMore) {
      this.showToast('没有更多数据了');
      return;
    }
    
    // 模拟请求更多数据
    await this.sleep(1000);
    
    const moreData = Array.from({ length: 20 }, (_, i) => ({
      id: this.data.length + i,
      text: `第${this.currentPage}页 Item ${i + 1}`,
      color: this.getRandomColor()
    }));
    
    // 添加到数据尾部
    this.data.push(...moreData);
    this.currentPage++;
    this.hasMore = this.currentPage < 5;  // 假设最多5页
    
    return {
      types: moreData.map(() => 'default')
    };
  }
});

性能测试

在标准移动设备上的性能表现:

数据量首屏渲染滚动帧率内存占用
100< 50ms60fps~5MB
1000< 100ms60fps~8MB
10000< 200ms60fps~15MB

相关示例