首页 > 其他 > 详细

Cocos Creator 组件ListView

时间:2020-07-09 09:19:39      阅读:78      评论:0      收藏:0      [点我收藏+]

版本: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

Cocos Creator 组件ListView

原文:https://www.cnblogs.com/gamedaybyday/p/13270209.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!