creator3.x的ListView分享
官方的ScrollView加载十几个就很卡了,还是用这个。


import { Component, instantiate, log, Node, NodePool, ScrollView, UITransform, Vec3, _decorator } from 'cc';
const { ccclass, property } = _decorator;

export interface ListViewDelegateV<T> {
    items(): T[];
    reuse?(itemNode: Node, item: T): void;
    unuse?(itemNode: Node): void;
}

// temp val
const itemIndex = new Set<number>();
const vec3 = new Vec3();

@ccclass('ListViewV')
export class ListViewV extends Component {
    @property(Node) itemTemplate: Node;
    @property spaceY = 0;
    scrollView: ScrollView;
    private itemHeight: number;
    private itemPool: NodePool = new NodePool();
    private dataSource: any[] = [];
    private visibleHeight: number;
    private spawnCount: number;
    private visibleNodes = new Map<number, Node>();
    private delegate: ListViewDelegateV<any> = { items: () => [] };

    onLoad() {
        this.scrollView = this.getComponent(ScrollView);
        this.itemHeight = this.itemTemplate.getComponent(UITransform).height;
        this.itemPool.put(this.itemTemplate);
        this.visibleHeight = this.node.getComponent(UITransform).height;
        this.spawnCount = Math.round(this.visibleHeight / this.itemHeight) + 1;
        this.ensure(this.spawnCount);
        log('ListView visibleHeight:', this.visibleHeight, 'spawnCount:', this.spawnCount, 'itemHeight:', this.itemHeight);
    }

    setDelegate<T>(delegate: ListViewDelegateV<T>) {
        this.delegate = delegate;
    }

    reload() {
        this.dataSource = this.delegate.items();
        const totalHeight = this.itemHeight * this.dataSource.length + (this.spaceY * Math.max(0, this.dataSource.length - 1));
        this.scrollView.content.getComponent(UITransform).height = totalHeight;
        const children = this.scrollView.content.children.slice();
        children.forEach(c => this.itemPool.put(c));  // [warning] not call unuse
        // @ts-ignore
        this.scrollView.content._children.length = 0;
        this.visibleNodes.clear();
        this.scrollView.stopAutoScroll();
        this.scrollView.scrollToTop(0, false);
        this.lastY = Number.MIN_SAFE_INTEGER;
        log('ListView reload totalCount:', this.dataSource.length);
    }

    ensure(count: number) {
        while (this.itemPool.size() < count) {
            this.itemPool.put(instantiate(this.itemTemplate));
        }
    }

    lastY: number = Number.MIN_SAFE_INTEGER;
    lateUpdate() {
        const y = Math.floor(this.scrollView.getScrollOffset().y);
        if (this.lastY != y) {
            this.lastY = y;
            this.getVisibleItemIndex(y);
            this.visibleNodes.forEach((node, idx) => {
                if (!itemIndex.has(idx)) {
                    this.delegate.unuse && this.delegate.unuse(node);
                    this.itemPool.put(node);
                    this.visibleNodes.delete(idx);
                }
            });
            itemIndex.forEach((idx) => {
                if (!this.visibleNodes.has(idx)) {
                    this.ensure(1);
                    const node = this.itemPool.get();
                    node.getPosition(vec3);
                    vec3.y = (-idx - 0.5) * this.itemHeight - this.spaceY * idx;
                    node.setPosition(vec3);
                    node.parent = this.scrollView.content;
                    this.delegate.reuse && this.delegate.reuse(node, this.dataSource[idx]);
                    this.visibleNodes.set(idx, node);
                }
            });
        }
    }

    getVisibleItemIndex(y: number) {
        let minY = Math.max(0, Math.floor(y / (this.itemHeight + this.spaceY)));
        let maxY = minY == 0 ? this.spawnCount : Math.round((y + this.visibleHeight) / (this.itemHeight + this.spaceY));
        const totalCount = this.dataSource.length;
        maxY = Math.min(maxY, totalCount);
        if (maxY == totalCount) {
            minY = Math.max(0, totalCount - this.spawnCount);
        }
        itemIndex.clear();
        for (let i = minY; i < minY + this.spawnCount && i < totalCount; i++) {
            itemIndex.add(i);
        }
    }
}

标签: none

添加新评论