热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何在CocosCreator中做一个List

这篇文章主要介绍了如何在CocosCreator中做一个List,对List列表感兴趣的同学,不妨来试验一下

CocosCreator版本:2.3.4

cocos没有List组件,所以要自己写。从cocos的example项目中找到assets/case/02_ui/05_listView的demo来改造。

自写一个虚拟列表,有垂直布局,水平布局,网格布局和Padding的List

Demo地址:https://files-cdn.cnblogs.com/files/gamedaybyday/cocos2.3.4_ListViewDemo_Grid.7z

cocos原来的LayOut做列表,有100个数据就有100个实例(左侧图)。

而虚拟列表则只有你看见的实例存在,当滑动时会循环使用。(右侧图)

List使用方法

使用方法就是在ScrollView上添加List组件即可。

List的item列表项直接放在content下,并赋值給List组件,item需要添加继承自ItemRender的对象,用于数据刷新。

代码中给List设置数据

//设置排行榜数据
let rankData = [];
 for(let i=0;i<100;i++){
      rankData.push({rank:i, name:"名称"});
}
 
this.rankList.setData(rankData);

源码

// Learn TypeScript:
//  - https://docs.cocos.com/creator/manual/en/scripting/typescript.html
// Learn Attribute:
//  - https://docs.cocos.com/creator/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
//  - https://docs.cocos.com/creator/manual/en/scripting/life-cycle-callbacks.html
 
import ItemRender from "./ItemRender"
 
const { ccclass, property } = cc._decorator;
 
/**列表排列方式 */
export enum ListType {
    /**水平排列 */
    HorizOntal= 1,
    /**垂直排列 */
    Vertical = 2,
    /**网格排列 */
    Grid = 3
}
 
/**网格布局中的方向 */
export enum StartAxisType {
    /**水平排列 */
    HorizOntal= 1,
    /**垂直排列 */
    Vertical = 2,
}
 
/**
 * 列表
 * 根据cocos_example的listView改动而来
 * @author chenkai 2020.7.8
 * @example
 *  1.创建cocos的ScrollView组件,添加List,设置List属性即可
 * 
 */
@ccclass
export default class List extends cc.Component {
 
    //==================== 属性面板 =========================
    /**列表选项 */
    @property({ type: cc.Node, tooltip: "列表项" })
    public itemRender: cc.Node = null;
 
    /**排列方式 */
    @property({ type: cc.Enum(ListType), tooltip: "排列方式" })
    public type: ListType = ListType.Vertical;
 
    /**网格布局中的方向 */
    @property({ type: cc.Enum(StartAxisType), tooltip: "网格布局中的方向", visible() { return this.type == ListType.Grid } })
    public startAxis: StartAxisType = StartAxisType.Horizontal;
 
    /**列表项之间X间隔 */
    @property({ type: cc.Integer, tooltip: "列表项X间隔", visible() { return (this.type == ListType.Horizontal || this.type == ListType.Grid) } })
    public spaceX: number = 0;
 
    /**列表项之间Y间隔 */
    @property({ type: cc.Integer, tooltip: "列表项Y间隔", visible() { return this.type == ListType.Vertical || this.type == ListType.Grid } })
    public spaceY: number = 0;
 
    /**上间距 */
    @property({ type: cc.Integer, tooltip: "上间距", visible() { return (this.type == ListType.Vertical || this.type == ListType.Grid) } })
    public padding_top: number = 0;
 
    /**下间距 */
    @property({ type: cc.Integer, tooltip: "下间距", visible() { return (this.type == ListType.Vertical || this.type == ListType.Grid) } })
    public padding_buttom: number = 0;
 
    /**左间距 */
    @property({ type: cc.Integer, tooltip: "左间距", visible() { return (this.type == ListType.Horizontal || this.type == ListType.Grid) } })
    public padding_left: number = 0;
 
 
 
    @property(cc.Integer)
    public _padding: number = 0;
 
    /**右间距 */
    @property({ type: cc.Integer, tooltip: "右间距", visible() { return (this.type == ListType.Horizontal || this.type == ListType.Grid) } })
    public padding_right: number = 0;
 
    //====================== 滚动容器 ===============================
    /**列表滚动容器 */
    public scrollView: cc.ScrollView = null;
    /**scrollView的内容容器 */
    private content: cc.Node = null;
 
    //======================== 列表项 ===========================
    /**列表项数据 */
    private itemDataList: Array = [];
    /**应创建的实例数量 */
    private spawnCount: number = 0;
    /**存放列表项实例的数组 */
    private itemList: Array = [];
    /**item的高度 */
    private itemHeight: number = 0;
    /**item的宽度 */
    private itemWidth: number = 0;
    /**存放不再使用中的列表项 */
    private itemPool: Array = [];
 
    //======================= 计算参数 ==========================
    /**距离scrollView中心点的距离,超过这个距离的item会被重置,一般设置为 scrollVIew.height/2 + item.heigt/2 + space,因为这个距离item正好超出scrollView显示范围 */
    private halfScrollView: number = 0;
    /**上一次content的X值,用于和现在content的X值比较,得出是向左还是向右滚动 */
    private lastContentPosX: number = 0;
    /**上一次content的Y值,用于和现在content的Y值比较,得出是向上还是向下滚动 */
    private lastContentPosY: number = 0;
    /**网格行数 */
    private gridRow: number = 0;
    /**网格列数 */
    private gridCol: number = 0;
    /**刷新时间,单位s */
    private updateTimer: number = 0;
    /**刷新间隔,单位s */
    private updateInterval: number = 0.1;
    /**是否滚动容器 */
    private bScrolling: boolean = false;
    /**刷新的函数 */
    private updateFun: Function = function () { };
 
    onLoad() {
        this.itemHeight = this.itemRender.height;
        this.itemWidth = this.itemRender.width;
        this.scrollView = this.node.getComponent(cc.ScrollView);
        this.cOntent= this.scrollView.content;
        this.content.anchorX = 0;
        this.content.anchorY = 1;
        this.content.removeAllChildren();
        this.scrollView.node.on("scrolling", this.onScrolling, this);
    }
 
    /**
     * 列表数据 (列表数据复制使用,如果列表数据改变,则需要重新设置一遍数据)
     * @param itemDataList item数据列表
     */
    public setData(itemDataList: Array) {
        this.itemDataList = itemDataList.slice();
        this.updateContent();
    }
 
    /**计算列表的各项参数 */
    private countListParam() {
        let dataLen = this.itemDataList.length;
        if (this.type == ListType.Vertical) {
            this.scrollView.horizOntal= false;
            this.scrollView.vertical = true;
            this.content.width = this.content.parent.width;
            this.content.height = dataLen * this.itemHeight + (dataLen - 1) * this.spaceY + this.padding_top + this.padding_buttom;
            this.spawnCount = Math.round(this.scrollView.node.height / (this.itemHeight + this.spaceY)) + 2; //计算创建的item实例数量,比当前scrollView容器能放下的item数量再加上2个
            this.halfScrollView = this.scrollView.node.height / 2 + this.itemHeight / 2 + this.spaceY; //计算bufferZone,item的显示范围
            this.updateFun = this.updateV;
        } else if (this.type == ListType.Horizontal) {
            this.scrollView.horizOntal= true;
            this.scrollView.vertical = false;
            this.content.width = dataLen * this.itemWidth + (dataLen - 1) * this.spaceX + this.padding_left + this.padding_right;
            this.content.height = this.content.parent.height;
            this.spawnCount = Math.round(this.scrollView.node.width / (this.itemWidth + this.spaceX)) + 2;
            this.halfScrollView = this.scrollView.node.width / 2 + this.itemWidth / 2 + this.spaceX;
            this.updateFun = this.udpateH;
        } else if (this.type == ListType.Grid) {
            if (this.startAxis == StartAxisType.Vertical) {
                this.scrollView.horizOntal= false;
                this.scrollView.vertical = true;
                this.content.width = this.content.parent.width;
                //如果left和right间隔过大,导致放不下一个item,则left和right都设置为0,相当于不生效
                if (this.padding_left + this.padding_right + this.itemWidth + this.spaceX > this.content.width) {
                    this.padding_left = 0;
                    this.padding_right = 0;
                    console.error("padding_left或padding_right过大");
                }
 
                this.gridCol = Math.floor((this.content.width - this.padding_left - this.padding_right) / (this.itemWidth + this.spaceX));
                this.gridRow = Math.ceil(dataLen / this.gridCol);
                this.content.height = this.gridRow * this.itemHeight + (this.gridRow - 1) * this.spaceY + this.padding_top + this.padding_buttom;
                this.spawnCount = Math.round(this.scrollView.node.height / (this.itemHeight + this.spaceY)) * this.gridCol + this.gridCol * 2;
                this.halfScrollView = this.scrollView.node.height / 2 + this.itemHeight / 2 + this.spaceY;
                this.updateFun = this.updateGrid_V;
            } else if (this.startAxis == StartAxisType.Horizontal) {
                this.scrollView.horizOntal= true;
                this.scrollView.vertical = false;
                //计算高间隔
                this.content.height = this.content.parent.height;
                //如果left和right间隔过大,导致放不下一个item,则left和right都设置为0,相当于不生效
                if (this.padding_top + this.padding_buttom + this.itemHeight + this.spaceY > this.content.height) {
                    this.padding_top = 0;
                    this.padding_buttom = 0;
                    console.error("padding_top或padding_buttom过大");
                }
 
                this.gridRow = Math.floor((this.content.height - this.padding_top - this.padding_buttom) / (this.itemHeight + this.spaceY));
                this.gridCol = Math.ceil(dataLen / this.gridRow);
                this.content.width = this.gridCol * this.itemWidth + (this.gridCol - 1) * this.spaceX + this.padding_left + this.padding_right;
                this.spawnCount = Math.round(this.scrollView.node.width / (this.itemWidth + this.spaceX)) * this.gridRow + this.gridRow * 2;
                this.halfScrollView = this.scrollView.node.width / 2 + this.itemWidth / 2 + this.spaceX;
                this.updateFun = this.updateGrid_H;
            }
        }
    }
 
    /**
     * 创建列表
     * @param startIndex 起始显示的数据索引 0表示第一项
     * @param offset     scrollView偏移量
     */
    private createList(startIndex: number, offset: cc.Vec2) {
        //当需要显示的数据长度 > 虚拟列表长度, 删除最末尾几个数据时,列表需要重置位置到scrollView最底端
        if (this.itemDataList.length > this.spawnCount && (startIndex + this.spawnCount - 1) >= this.itemDataList.length) {
            startIndex = this.itemDataList.length - this.spawnCount;
            offset = this.scrollView.getMaxScrollOffset();
 
            //当需要显示的数据长度 <= 虚拟列表长度, 隐藏多余的虚拟列表项
        } else if (this.itemDataList.length <= this.spawnCount) {
            startIndex = 0;
        }
 
        for (let i = 0; i  需要显示的数据量
                if (this.itemList.length > (this.itemDataList.length - startIndex)) {
                    item = this.itemList.pop();
                    item.removeFromParent();
                    this.itemPool.push(item);
                }
                continue;
            }
 
            let itemRender: ItemRender = item.getComponent(ItemRender);
            itemRender.itemIndex = i + startIndex;
            itemRender.data = this.itemDataList[i + startIndex];
            itemRender.dataChanged();
 
            if (this.type == ListType.Vertical) {
                //因为content的锚点X是0,所以item的x值是content.with/2表示居中,锚点Y是1,所以item的y值从content顶部向下是0到负无穷。所以item.y= -item.height/2时,是在content的顶部。
                item.setPosition(this.content.width / 2, -item.height * (0.5 + i + startIndex) - this.spaceY * (i + startIndex) - this.padding_top);
            } else if (this.type == ListType.Horizontal) {
                item.setPosition(item.width * (0.5 + i + startIndex) + this.spaceX * (i + startIndex) + this.padding_left, -this.content.height / 2);
            } else if (this.type == ListType.Grid) {
                if (this.startAxis == StartAxisType.Vertical) {
                    var row = Math.floor((i + startIndex) / this.gridCol);
                    var col = (i + startIndex) % this.gridCol;
                    item.setPosition(item.width * (0.5 + col) + this.spaceX * col + this.padding_left, -item.height * (0.5 + row) - this.spaceY * row - this.padding_top);
                    item.opacity = 255;
                } else if (this.startAxis == StartAxisType.Horizontal) {
                    var row = (i + startIndex) % this.gridRow;
                    var col = Math.floor((i + startIndex) / this.gridRow);
                    item.setPosition(item.width * (0.5 + col) + this.spaceX * col + this.padding_left, -item.height * (0.5 + row) - this.spaceY * row - this.padding_top);
                    item.opacity = 255;
                }
            }
        }
 
        this.scrollView.scrollToOffset(offset);
    }
 
    /**获取一个列表项 */
    private getItem() {
        if (this.itemPool.length == 0) {
            return cc.instantiate(this.itemRender);
        } else {
            return this.itemPool.pop();
        }
    }
 
    update(dt) {
        if (this.bScrolling == false) {
            return;
        }
        this.updateTimer += dt;
        if (this.updateTimer  this.lastContentPosY;
        let offset = (this.itemHeight + this.spaceY) * items.length;
        for (let i = 0; i  bufferZone && item.y - offset - this.padding_buttom > -this.content.height) {
                    let itemRender: ItemRender = item.getComponent(ItemRender);
                    let itemIndex = itemRender.itemIndex + items.length;
                    itemRender.itemIndex = itemIndex;
                    itemRender.data = this.itemDataList[itemIndex];
                    itemRender.dataChanged();
                    item.y = item.y - offset;
                }
            } else {
                //item下滑时,超出了scrollView下边界,将item移动到上方复用,item移动到上方的位置必须不超过content的上边界
                if (viewPos.y <-bufferZone && item.y + offset + this.padding_top <0) {
                    let itemRender: ItemRender = item.getComponent(ItemRender);
                    let itemIndex = itemRender.itemIndex - items.length;
                    itemRender.itemIndex = itemIndex;
                    itemRender.data = this.itemDataList[itemIndex];
                    itemRender.dataChanged();
                    item.y = item.y + offset;
                }
            }
        }
        this.lastCOntentPosY= this.scrollView.content.y;
    }
 
    /**水平排列 */
    private udpateH() {
        let items = this.itemList;
        let item;
        let bufferZOne= this.halfScrollView;
        let isRight = this.scrollView.content.x > this.lastContentPosX;
        let offset = (this.itemWidth + this.spaceX) * items.length;
        for (let i = 0; i  bufferZone && item.x - offset - this.padding_left > 0) {
                    let itemRender: ItemRender = item.getComponent(ItemRender);
                    let itemIndex = itemRender.itemIndex - items.length;
                    itemRender.itemIndex = itemIndex;
                    itemRender.data = this.itemDataList[itemIndex];
                    itemRender.dataChanged();
                    item.x = item.x - offset;
                }
            } else {
                //item左滑时,超出了scrollView左边界,将item移动到右方复用,item移动到右方的位置必须不超过content的右边界
                if (viewPos.x <-bufferZone && item.x + offset + this.padding_right  this.lastContentPosY;
        let offset = (this.itemHeight + this.spaceY) * (this.spawnCount / this.gridCol);
        for (let i = 0; i  bufferZone && item.y - offset - this.padding_buttom > -this.content.height) {
                    let itemRender: ItemRender = item.getComponent(ItemRender);
                    let itemIndex = itemRender.itemIndex + (this.spawnCount / this.gridCol) * this.gridCol;
                    if (this.itemDataList[itemIndex] != null) {
                        item.y = item.y - offset;
                        itemRender.itemIndex = itemIndex;
                        itemRender.data = this.itemDataList[itemIndex];
                        itemRender.dataChanged();
                        item.opacity = 255;
                    } else {
                        item.y = item.y - offset;
                        itemRender.itemIndex = itemIndex;
                        item.opacity = 0;
                    }
                }
            } else {//item下滑时,超出了scrollView下边界,将item移动到上方复用,item移动到上方的位置必须不超过content的上边界
                if (viewPos.y <-bufferZone && item.y + offset + this.padding_top <0) {
                    let itemRender: ItemRender = item.getComponent(ItemRender);
                    let itemIndex = itemRender.itemIndex - (this.spawnCount / this.gridCol) * this.gridCol;
                    if (this.itemDataList[itemIndex] != null) {
                        item.y = item.y + offset;
                        itemRender.itemIndex = itemIndex;
                        itemRender.data = this.itemDataList[itemIndex];
                        itemRender.dataChanged();
                        item.opacity = 255;
                    } else {
                        item.y = item.y + offset;
                        itemRender.itemIndex = itemIndex;
                        item.opacity = 0;
                    }
                }
            }
        }
        this.lastCOntentPosY= this.scrollView.content.y;
    }
 
    /**网格水平排列 */
    private updateGrid_H() {
        let items = this.itemList;
        let item;
        let bufferZOne= this.halfScrollView;
        let isRight = this.scrollView.content.x > this.lastContentPosX;
        let offset = (this.itemWidth + this.spaceX) * (this.spawnCount / this.gridRow);
        for (let i = 0; i  bufferZone && item.x - offset - this.padding_left > 0) {
                    let itemRender: ItemRender = item.getComponent(ItemRender);
                    let itemIndex = itemRender.itemIndex - (this.spawnCount / this.gridRow) * this.gridRow;
                    if (this.itemDataList[itemIndex] != null) {
                        item.x = item.x - offset;
                        itemRender.itemIndex = itemIndex;
                        itemRender.data = this.itemDataList[itemIndex];
                        itemRender.dataChanged();
                        item.opacity = 255;
                    } else {
                        item.x = item.x - offset;
                        itemRender.itemIndex = itemIndex;
                        item.opacity = 0;
                    }
                }
            } else {
                //item左滑时,超出了scrollView左边界,将item移动到右方复用,item移动到右方的位置必须不超过content的右边界
                if (viewPos.x <-bufferZone && item.x + offset + this.padding_right  {
                    return b.y - a.y;
                });
            } else if (this.type == ListType.Horizontal) {
                this.itemList.sort((a: any, b: any) => {
                    return a.x - b.x;
                });
            } else if (this.type == ListType.Grid) {
                if (this.startAxis == StartAxisType.Vertical) {
                    this.itemList.sort((a: any, b: any) => {
                        return a.x - b.x;
                    });
                    this.itemList.sort((a: any, b: any) => {
                        return b.y - a.y;
                    });
                } else if (this.startAxis == StartAxisType.Horizontal) {
                    this.itemList.sort((a: any, b: any) => {
                        return b.y - a.y;
                    });
                    this.itemList.sort((a: any, b: any) => {
                        return a.x - b.x;
                    });
                }
            }
 
            this.countListParam();
 
            //获取第一个item实例需要显示的数据索引
            var startIndex = this.itemList[0].getComponent(ItemRender).itemIndex;
 
            if (this.type == ListType.Grid && this.startAxis == StartAxisType.Vertical) {
                startIndex += (startIndex + this.spawnCount) % this.gridCol;
            } else if (this.type == ListType.Grid && this.startAxis == StartAxisType.Horizontal) {
                startIndex += (startIndex + this.spawnCount) % this.gridRow;
            }
 
            //getScrollOffset()和scrollToOffset()的x值是相反的
            var offset: cc.Vec2 = this.scrollView.getScrollOffset();
            offset.x = - offset.x;
 
            this.createList(startIndex, offset);
        }
    }
 
    /**销毁 */
    public onDestroy() {
        //清理列表项
        let len = this.itemList.length;
        for (let i = 0; i 

以上就是如何在CocosCreator中做一个List的详细内容,更多关于CocosCreator List的资料请关注其它相关文章!


推荐阅读
  • 本文介绍了Java的集合及其实现类,包括数据结构、抽象类和具体实现类的关系,详细介绍了List接口及其实现类ArrayList的基本操作和特点。文章通过提供相关参考文档和链接,帮助读者更好地理解和使用Java的集合类。 ... [详细]
  • 本文介绍了Cocos2dx学习笔记中的更新函数scheduleUpdate、进度计时器CCProgressTo和滚动视图CCScrollView的用法。详细介绍了scheduleUpdate函数的作用和使用方法,以及schedule函数的区别。同时,还提供了相关的代码示例。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 实例详解ISA防火墙策略元素
    我们在前面的工作中已经实现了ISA2006的代理服务器功能,接下来我们要实现ISA的访问控制功能。很多公司都有控制员工访问外网的需求,例如有的公司不允许 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • HSRP热备份路由器协议的应用及配置
    本文介绍了HSRP热备份路由器协议的应用及配置方法,包括设计目标、工作原理、配置命令等。通过HSRP协议,可以实现在主动路由器故障时自动切换到备份路由器,保证网络连通性。此外,还介绍了R1和R2路由器的配置方法以及Sw1和Sw2交换机的配置方法,最后还介绍了测试连通性和路由追踪的方法。 ... [详细]
  • 介绍一款好用的内网穿透工具FRP
    本文介绍了一款好用的内网穿透工具FRP,它是一个使用Go语言开发的高性能的反向代理应用。FRP支持多种协议类型,并且可以根据域名进行路由转发。 ... [详细]
  • Linux防火墙配置—允许转发
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 三、寻找恶意IP并用iptables禁止掉找出恶意连接你的服务器80端口的IP,直接用iptables来drop掉它;这里建议写脚本来运行, ... [详细]
  • 业务:Payments&Risk大数据/AI/数据可视化时间要求:至少实习6个月,每周5天,入职时间4-5月 ... [详细]
  • 【node.js】关于node.js,如何解决npm should be run outside of the Node.js REPL, in your normal shell报错?
    问题描述前言,安装node方式采用的是安装包解压的报如下错误:npmshouldberunoutsideoftheNode.jsREPL,inyournormals ... [详细]
  • Vue3 拥抱 TypeScript 的完整项目结构搭建
    大厂技术高级前端Node进阶点击上方程序员成长指北,关注公众号回复1,加入高级Node交流群一个完整的Vue3Ts项目,支持.vue和.tsx写法项目地 ... [详细]
  • 我试图在Angular2应用程序中使用元素调整大小检测器库(https:github.comwnrelement-resize-detector).根据我有限的JS模块知识,该库似 ... [详细]
author-avatar
魏蚊瑞
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有