cocos creator scrollView 优化 item重复利用

1. ListItem基类

    


const {ccclass, property} = cc._decorator;

@ccclass
export default class ListViewItem extends cc.Component {

    itemId: number = 0;

    onLoad() {
        
    }

    /**
     * 不需要重写这个方法
     * @param index 索引从0开始
     * @param data 所有列表数据
     */
    initData(index: number, data: any) {
        this.itemId = index;
        this.init(index, data);
    }

    /**
     * 重写这个方法
     * 要在listview中子item继承这个ListViewItem
     * @param index  item索引从0开始
     * @param data  所有列表数据
     */
    init(index: number, data: any) {

    }
}

2. ListView 类

import ListViewItem from "./ListViewItem";

const {ccclass, property} = cc._decorator;

/**
 * 实现scrollview item循环滚动 ,目前只支持垂直 
 */


/**
 * count: length of listview
 * listViewData: data of listview
 */
type ListViewData = {
    count: number,
    listViewData: any
}

@ccclass
export default class ListView extends cc.Component {
    // @property(cc.Label)
    // itemCountLabel: cc.Label = null;

    @property({
        type: cc.Prefab,
        tooltip: "listview单元格,仅支持大小相等的单元格"
    })
    prefabCell: cc.Prefab = null; // listview单元格,仅支持大小相等的单元格

    @property({
        type: cc.ScrollView,
        tooltip: "找到scrollview的控件"
    })
    scrollView:cc.ScrollView = null; // 找到scrollview的控件

    @property(cc.Integer)
    spacing: number = 0; // listview单元格间距

    // @property(cc.String)
    // cellScriptName: string = "";

    @property({
        type: cc.Node,
        tooltip: "找到scrollview的content"
    })
    content: cc.Node = null; // 找到scrollview的content, 搭配scrollview使用

    @property({
        type: cc.Integer,
        tooltip: "缓冲区总个数,即上下缓冲区分别bufferZoneCount / 2"
    })
    bufferZoneCount: number = 4; // 缓冲区总个数,即上下缓冲区分别bufferZoneCount / 2

    items: cc.Node[] = [];

    itemCount: number = 0;
    prefabCellHeight: number = 0;
    prefabCellWidth: number = 0;

    updateTimer: number = 0;
    
    updateInterval: number = 0.1;
    
    lastContentPosX: number = 0;
    
    lastContentPosY: number = 0;
    
    _bufferZone: number = 0; //when item is away from bufferZone, we relocate it
    _spawnCount: number = 0; // how many items we actually spawn
    _totalCount: number = 0; // how many items we need for the whole list
    _isUpdate: boolean = false;
    _itemData: any;
    // _listViewItem: ListViewItem = new ListViewItem();

    onLoad () {
        this._bufferZone = this.scrollView.node.height;
        this.content.anchorX = 0.5;
        this.content.anchorY = 1;
        if (this.scrollView.horizontal) {
            this._bufferZone = this.scrollView.node.width;
            this.content.anchorX = 0;
            this.content.anchorY = 0.5;
        }
        this.prefabCellHeight = this.prefabCell.data.height;
        this.prefabCellWidth = this.prefabCell.data.width;
        this.scrollView.node.on('scrolling', this.scrolling.bind(this), this);
        this.scrollView.node.on('scroll-ended', this.scrollEnded.bind(this), this);
        // this.init({count: 20, listViewData: null});
    }

    /**
     * 初始化ListView组件
     * @param listViewData ListViewData
     */
    public init (listViewData: ListViewData) {
        this.content.destroyAllChildren();
        this.items = [];
        this.lastContentPosY = 0;
        this.lastContentPosX = 0;
        this.itemCount = listViewData.count;
        this._itemData = listViewData.listViewData;

        if (this.scrollView.vertical && this.scrollView.horizontal) {
            return;
        }

        this._totalCount = this.itemCount;
        this._spawnCount = Math.floor(this._bufferZone / (this.prefabCellHeight + this.spacing)) + this.bufferZoneCount;
        if (this.scrollView.horizontal) {
            this._spawnCount = Math.floor(this._bufferZone / (this.prefabCellWidth + this.spacing)) + this.bufferZoneCount;
        }

        if (this._spawnCount > this._totalCount) {
            this._spawnCount = this._totalCount;
        }

        if (this.scrollView.vertical) {
            this.scrollView.scrollToTop();
            this.content.height = this._totalCount * (this.prefabCellHeight + this.spacing) + this.spacing; // get total content height
        } else if (this.scrollView.horizontal) {
            this.scrollView.scrollToLeft();
            this.content.width = this._totalCount * (this.prefabCellWidth + this.spacing) + this.spacing; // get total content width
        }
        for (let i = 0; i < this._spawnCount; ++i) { // spawn items, we only need to do this once
            let item = cc.instantiate(this.prefabCell);
            this.content.addChild(item);
            item.getComponent(ListViewItem).initData(i, this._itemData);

            if (this.scrollView.vertical) {
                item.setPosition(0, -item.height * (0.5 + i) - this.spacing * (i + 1));
            } else if (this.scrollView.horizontal) {
                item.setPosition(item.width * (i + 0.5) + this.spacing * (i + 1), 0);
            }
            this.items.push(item);
        }
    }

    /**
     * 获取item在scrollview中的位置
     * @param item item位置
     */
    private getPositionInView (item) { // get item position in scrollview's node space
        let worldPos = item.parent.convertToWorldSpaceAR(item.position);
        let viewPos = this.scrollView.node.convertToNodeSpaceAR(worldPos);
        return viewPos;
    }

    /**
     * 监听:滚动时更新
     */
    private scrolling(){
        this._isUpdate = true;
    }

    /**
     * 监听:滚动停止时不执行逻辑
     */
    private scrollEnded() {
        this._isUpdate = false;
    }

    

    update (dt) {
        // this.itemCountLabel.string = (this.content.childrenCount).toString();
        
        // scrolling excute or don't excute
        if (!this._isUpdate) return;

        this.updateTimer += dt;
        if (this.updateTimer < this.updateInterval) return; // we don't need to do the math every frame
        this.updateTimer = 0;
        let items = this.items;
        let buffer = this._bufferZone;
        if (this.scrollView.vertical) {
            let isDown = this.scrollView.content.y < this.lastContentPosY; // scrolling direction
            let offset = (this.prefabCellHeight + this.spacing) * items.length;
            for (let i = 0; i < items.length; ++i) {
                let viewPos = this.getPositionInView(items[i]);
                let item = items[i].getComponent(ListViewItem);
                if (isDown) {
                    // if away from buffer zone and not reaching top of content
                    if (viewPos.y < -buffer && items[i].y + offset < 0) {
                        items[i].y = items[i].y + offset;
                        items[i].getComponent(ListViewItem).initData(item.itemId -items.length, this._itemData);
                    }
                } else {
                    // if away from buffer zone and not reaching bottom of content
                    if (viewPos.y > buffer / 2 + (this.prefabCellHeight + this.spacing) / 2 && items[i].y - offset > -this.content.height) {
                        items[i].y = items[i].y - offset;
                        items[i].getComponent(ListViewItem).initData(item.itemId + items.length, this._itemData);
                    }
                }
            }
            // update lastContentPosY
            this.lastContentPosY = this.scrollView.content.y;
        } else if (this.scrollView.horizontal) {
            let isRight = this.scrollView.content.x > this.lastContentPosX; // scrolling direction
            let offset = (this.prefabCellWidth + this.spacing) * items.length;
            for (let i = 0; i < items.length; ++i) {
                let viewPos = this.getPositionInView(items[i]);
                if (isRight) {
                    // if away from buffer zone and not reaching top of content
                    if (viewPos.x > buffer / 2 + (this.prefabCellWidth + this.spacing) / 2 && items[i].x - offset > 0) {
                        items[i].x = items[i].x - offset;
                    }
                } else {
                    // if away from buffer zone and not reaching bottom of content
                    if (viewPos.x < -buffer / 2 - (this.prefabCellWidth + this.spacing) / 2 && items[i].x + offset < this.content.width) {
                        items[i].x = items[i].x + offset;
                    }
                }
            }
            // update lastContentPosX
            this.lastContentPosX = this.scrollView.content.x;
        }
    }
};

3. 使用如图

 

ListView 绑定到 ScrollView节点下,UIFreeGoldItem 为滚动条,其类继承ListIViewitem。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值