• Cocos Creator 组件ListView


    版本:2.3.4

    cocos没有List组件,所以要自己写。

    从cocos的example项目中找到listView的demo来改造

    新修改的ListView对比原来有以下改动:

    1. 去掉了totalCount、spawnCount和bufferZone的计算,根据实际情况自动计算合适的值。

    2. 增加了列表项数据的传入和刷新。例如排行榜做列表,可以传入排行榜数据[{rank:1,name:"啊",{rank:2,name:"啦"},...}]来显示。

    3. 将ListViewCtrl和ScrollView两个Node,合成一个Node,做成prefab,并拖动到自定义控件区域,以备复用。

    4. 去掉了addItem和removeItem,列表项由传入的data决定。

    5. 增加了ListItem类来处理data数据的显示。

    UI结构如下图:

    对比cocos example的,ListView所有UI在一个节点上,方便做成预制件。

    ListView类:

    import ListItem from "./ListItem";
    
    const {ccclass, property} = cc._decorator;
    
    /**
     * 列表
     * 根据cocos_example的listView改动而来
     * @author chenkai 2020.7.8
     */
    @ccclass
    export default class ListView extends cc.Component {
    
        /**列表选项 */
        @property(cc.Node)
        public item:cc.Node = null;
    
        /**列表选项类 */
        @property(cc.String)
        public itemClass:string = "";
    
        /**列表滚动容器 */
        @property(cc.ScrollView)
        public scrollView:cc.ScrollView = null;
    
        /**列表项之间间隔 */
        @property(cc.Integer)
        public spacing:number = 0;
    
        /**列表项实例数量 */
        private spawnCount:number = 0;
        /**距离scrollView中心点的距离,超过这个距离的item会被重置,一般设置为 scrollVIew.height/2 + item.heigt/2 + spaceing,因为这个距离item正好超出scrollView显示范围 */
        private bufferZone:number = 0;
        /**列表项总数 */
        public totalCount:number = 0;
        /**scrollView的内容容器 */
        private content:cc.Node = null;
        /**存放列表项实例的数组 */
        private items:Array<cc.Node> = [];
        /**刷新列表计时 */
        private updateTimer:number = 0;
        /**刷新列表间隔 */
        private updateInterval:number = 0;
        /**上一次content的Y值,用于和现在content的Y值比较,得出是向上还是向下滚动 */
        private lastContentPosY:number = 0;
        /**列表项数据 */
        private itemDataList:any = [];
        /**item的高度 */
        private itemHeight:number = 0;
    
        onLoad() {
            //初始化
            this.content = this.scrollView.content;
            this.items = [];
            this.updateTimer = 0;
            this.updateInterval = 0.1;
            this.lastContentPosY = 0;
            this.itemHeight = this.item.height;
            this.content.removeAllChildren();
            
            //计算创建的item实例数量,比当前scrollView容器能放下的item数量再加上2个
            this.spawnCount = Math.round(this.scrollView.node.height/( this.itemHeight + this.spacing)) + 2;
            //计算bufferZone
            this.bufferZone = this.scrollView.node.height/2 +  this.itemHeight/2 + this.spacing;
            //暂停滚动
            this.enabled = false;
            this.scrollView.enabled = false;
        }
    
        /**
         * 设置item的数据
         * @example
         *   setData([{id:1,msg:"a"},{id:2,msg:"b"}])
         * @param itemDataList item数据列表
         */
        public setData(itemDataList:any){
            //复制item数据,如果item数据源改变,则需要重新setData一次来显示新数据
            this.itemDataList = itemDataList.slice();
            this.totalCount = this.itemDataList.length;
            this.createItem();
            //运行滚动
            this.enabled = true;
            this.scrollView.enabled = true;
        }
        
        /**创建item实例 */
        private createItem () {
            this.content.height = this.totalCount * ( this.itemHeight + this.spacing) + this.spacing;
            this.clearAllItem();
            let len = this.totalCount < this.spawnCount?this.totalCount:this.spawnCount;
            for (let i = 0; i < len; i++) { // spawn items, we only need to do this once
                let item = cc.instantiate(this.item);
                this.content.addChild(item);
                item.setPosition(0, -item.height * (0.5 + i) - this.spacing * (i + 1));
                item.getComponent(this.itemClass).updateItem(i, this.itemDataList[i]);
                this.items.push(item);
            }
        }
    
        /**清理item实例 */
        private clearAllItem(){
            for(let i=0,len=this.items.length;i<len;i++){
                let item = this.items[i];
                item.destroy();
            }
            this.items.length = 0;
        }
        
        /**获取item在scrollView的局部坐标 */
        private getPositionInView(item) { 
            let worldPos = item.parent.convertToWorldSpaceAR(item.position);
            let viewPos = this.scrollView.node.convertToNodeSpaceAR(worldPos);
            return viewPos;
        }
     
        update(dt) {
            this.updateTimer += dt;
            if (this.updateTimer < this.updateInterval) return;
            this.updateTimer = 0;
            let items = this.items;
            let buffer = this.bufferZone;
            let isDown = this.scrollView.content.y < this.lastContentPosY; // scrolling direction
            let offset = ( this.itemHeight + this.spacing) * items.length;
            for (let i = 0; i < items.length; ++i) {
                let viewPos = this.getPositionInView(items[i]);
                if (isDown) {
                    // if away from buffer zone and not reaching top of content
                    if (viewPos.y < -buffer && items[i].y + offset < 0) {
                        //console.log("更新A前,items[i]:" + i +"viewPos.y:",viewPos.y,"buffer:" ,buffer,"items[i].y:", items[i].y,"offset:",offset,"this.content.height:",this.content.height);
                        items[i].y = items[i].y + offset;
                        let item = items[i].getComponent(this.itemClass);
                        let itemId = item.itemID - items.length; // update item id
                        //item.updateItem(itemId);
                        item.updateItem(itemId,this.itemDataList[itemId]);
                        //console.log("更新A后,tmpID:",item.tmplID,"itemId:" ,itemId,"viewPosY:", viewPos.y,"buffer:",buffer,"offset:",offset);
                    }
                } else {
                    // if away from buffer zone and not reaching bottom of content
                    if (viewPos.y > buffer && items[i].y - offset > -this.content.height) {
                        //console.log("更新B前,items[i]:" + i +"viewPos.y:",viewPos.y,"buffer:" ,buffer,"items[i].y:", items[i].y,"offset:",offset,"this.content.height:",this.content.height);
                        items[i].y = items[i].y - offset;
                        let item = items[i].getComponent(this.itemClass);
                        let itemId = item.itemID + items.length;
                        //item.updateItem(itemId);
                        item.updateItem(itemId,this.itemDataList[itemId]);
                        //console.log("更新B后,tmpID:",item.tmplID,"itemId:" ,itemId,"viewPosY:", viewPos.y,"items[i].y:",items[i].y,"buffer:",buffer,"offset:",offset);
                    }
                }
            }
            // update lastContentPosY
            this.lastContentPosY = this.scrollView.content.y;
        }
        
        /**
         * 滚动到指定位置
         * @param vec2 位置
         */
        public scrollToFixedPosition (vec2:cc.Vec2) {
            this.scrollView.scrollToOffset(vec2, 2);
        }
    
        /**销毁 */
        public onDestroy(){
            
        }
    }
    

      

    ListItem类:

    const {ccclass, property} = cc._decorator;
    
    /**
     * 列表项基类
     * @author chenkai 2020.7.8
     */
    @ccclass
    export default class ListItem extends cc.Component {
        /**当前项ID,0表示第一项 */
        public itemID:number = 0;
        /**数据 */
        public data:any;
        
        /**
         * 刷新
         * @param itemID 当前项ID
         * @param data   数据
         */
        public updateItem(itemID, data) {
            this.itemID = itemID;
            this.data = data;
            this.dataChanged();
        }
    
        /**数据改变 */
        protected dataChanged(){
            
        }
    
    }
    

      

    RankListItem类:

    继承自ListItem,并重写了dataChanged方法,在Item上下滚动导致刷新时,重置文本。

    import ListItem from "./ListView/ListItem";
    
    const {ccclass, property} = cc._decorator;
    
    @ccclass
    export default class RankListItem extends ListItem {
        private rankLab:cc.Label;
        private nameLab:cc.Label;
    
        onLoad(){
            this.rankLab = cc.find("rankLab",this.node).getComponent(cc.Label);
            this.nameLab = cc.find("nameLab",this.node).getComponent(cc.Label);
        }
    
        protected dataChanged(){
            this.rankLab.string = "第" + this.data.rank;
            this.nameLab.string = this.data.name;
        }
    }
    

      

    HelloWorld代码中使用

    import ListView from "./ListView/ListView";
    
    const {ccclass, property} = cc._decorator;
    
    @ccclass
    export default class Helloworld extends cc.Component {
        //排行榜
        private rankListView:ListView;
    
        onLoad(){
            
        }
    
        start(){
            //获取排行榜ListView
            this.rankListView = cc.find("RankListView", this.node).getComponent(ListView);
    
            //设置排行榜数据
            let rankData = [{rank:1,name:"啊飞"},{rank:2,name:"肚肚"},{rank:3,name:"普洱"},
                            {rank:4,name:"电饭锅"},{rank:5,name:"松岛"},{rank:6,name:"撒嗄"}];
    
            this.rankListView.setData(rankData);
    
            //重新设置排行榜数据
            rankData = [{rank:1,name:"啊飞"},{rank:2,name:"肚肚"},{rank:3,name:"普洱"},
                        {rank:4,name:"电饭锅"},{rank:5,name:"松岛"},{rank:6,name:"撒嗄"},
                        {rank:7,name:"打手犯规"},{rank:8,name:"萨达"},{rank:9,name:"苟富贵"}];
    
            this.rankListView.setData(rankData);
            //销毁
            //this.rankListView.node.destroy();
        }
    }
    

      

    实际效果:

    Demo:

     https://files-cdn.cnblogs.com/files/gamedaybyday/ListViewDemo.7z

  • 相关阅读:
    一款单机游戏应该有的一些要素
    终于成功注册了Amazon.com的Affiliate
    创办公司的步骤不完全讲解(二)
    继续新环境没有asp.net mvc3项目模板的问题
    在自己的博客上打个广告,Kinect for Windows要的来
    数据仓库走向灭亡??
    Oracle & Endeca
    无题
    无题
    【译著】第7章 SportsStore:一个真实的应用程序 — 《精通ASP.NET MVC 3框架》
  • 原文地址:https://www.cnblogs.com/gamedaybyday/p/13270209.html
Copyright © 2020-2023  润新知