listView

// lqr:自己写的一个listview.挂载就能用,懂的都懂。
export class ListViewParam {

data:Array<any> = [];

itemLength:number = 0;

itemRenderHandler:cc.Component.EventHandler = null ;

itemRenderFunction:Function = null ;

itemPref:cc.Node = null ;

clearPool:boolean = false ;

}

const { ccclass, property, disallowMultiple, menu, executionOrder, requireComponent } = cc._decorator;

@ccclass

@disallowMultiple()

@menu(‘自定义组件/ListView’)

@requireComponent(cc.ScrollView)

@executionOrder(-5000)

export default class ListView extends cc.Component {

/**

 * @param itemRenderHandler 可以手动绑定回调事件,也可以在 refreshScrollView 里面传进来。

 */

@property({

    type: cc.Component.EventHandler,

    tooltip: CC_DEV && '渲染Item事件(渲染器)',

})

public itemRenderHandler: cc.Component.EventHandler = new cc.Component.EventHandler();

/**

 * @param itemPref 复用的item,可以手动绑定,也可以在 refreshScrollView 里面传进来,都不传默认是content的第一个子节点。

 */

@property({

    type:cc.Node,

    tooltip: CC_DEV && ' 复用的item,可以手动绑定,也可以在 refreshScrollView 里面传进来,都不传默认是content的第一个子节点。',

})

public itemPref:cc.Node = null;

/**

 * @param itemTotalNum item总共的长度,一般是数组的长度。

 */

@property({

    type:cc.Integer,

    tooltip: CC_DEV && 'item总共的长度,一般是数组的长度,refreshScrollView 里面传进来,如果是数组,则会自己计算。',

})

public itemTotalNum:number = 0 ;

/**

 * @param showItemNum 显示item的数量,可手动设置。如果为-1,会根据遮罩动态计算。

 */

@property({

    type:cc.Integer,

    tooltip: CC_DEV && '显示item的数量,可手动设置。如果为-1,会根据遮罩动态计算。',

})

public showItemNum:number = -1 ;

/**

 * @param scroll_com  ScrollView组件,可调用原有的方法。比如,滚动到底部,顶部等。

 */

public scroll_com:cc.ScrollView = null ;

init()

{

    if(this.isInit) return ;

    this.isInit = true ;

    this.scroll_com  = this.node.getComponent(cc.ScrollView); 

    // 添加事件

    let moveScroll = new cc.Component.EventHandler() ;

    moveScroll.target = this.node ;

    moveScroll.component = cc.js.getClassName(this) ;

    moveScroll.handler = "onScroll" ;

    this.node.getComponent(cc.ScrollView).scrollEvents.push( moveScroll );

    if( this.itemPref == null ) this.itemPref = this.scroll_com.content.children[0];

    this.itemPref.parent = null ;

    this.content = this.scroll_com.content ;

    this.nodePoolList = new cc.NodePool();

    if( this.showItemNum == -1 ) this.showItemNum = Math.ceil( this.node.getChildByName("view").height / this.itemPref.height );

}

private dataList:Array<any> = null ;

private maxItemIndex:number = 0 ;

private nowItemIndex:number = 0 ;

private minItemIndex:number = 0 ;

private showItemIndexList:Array<number> = [] ;

private showItemNodeList:Array<cc.Node> = [];

private content:cc.Node = null ;

private isInit:boolean = false;

private nodePoolList:cc.NodePool;

private getPoolNode()

{

    let node:cc.Node = null;

    if (this.nodePoolList.size() > 0) { // 通过 size 接口判断对象池中是否有空闲的对象

        node = this.nodePoolList.get();

    } else { // 如果没有空闲对象,也就是对象池中备用对象不够时,我们就用 cc.instantiate 重新创建

        node = cc.instantiate(this.itemPref);

    }

    node.active = true ;

    return node

}

private delPoolNode(node:cc.Node)

{

    node.parent = null ;

    node.active = false ;

    this.nodePoolList.put(node);

}

private clearPoolNode()

{

    this.nodePoolList.clear();

}

private onScroll(){

    let yy = ( this.content.y - this.itemPref.height )  / this.itemPref.height; 

    let index = Math.floor(yy);

    if( index < 0 ) index = 0 ;

    if(this.nowItemIndex == index) return ;

    this.nowItemIndex = index ;

    this.refreshItem();

}

private refreshItem()

{

    let index;

    if( this.nowItemIndex == this.minItemIndex) index = this.minItemIndex + 1 ;

    else if( this.nowItemIndex == this.maxItemIndex)  index = this.maxItemIndex - 1 ;

    else index = this.nowItemIndex ;

    let showItems = [] ;

    for( let i = index -2 ; i < index + this.showItemNum + 2  ; i ++ )

    {

        showItems.push(i);

    }

    for( let i = 0 ; i <  showItems.length  ; i ++ )

    {

        let isReset = false ;

        for( let j = 0 ; j < this.showItemIndexList.length  ; j ++ )

        {

            if( showItems[i] == this.showItemIndexList[j] )

            {

                isReset = true ;

            }

        }

        if( !isReset )  this.getItem(showItems[i]);

    }

    for( let i = 0 ; i <  this.showItemIndexList.length  ; i ++ )

    {

        let isReset = false ;

        for( let j = 0 ; j < showItems.length  ; j ++ )

        {

            if( this.showItemIndexList[i] == showItems[j] )

            {

                isReset = true ;

            }

        }

        if( !isReset )  this.delItem(i);

    }

}

private getItem(index)

{

    let node = this.getPoolNode();

    node.parent = this.content ;

    // 默认锚点是0.5 也可以支持 1 或者 0 ;

    if( node.anchorY == 1 ) node.y = - node.height * (index - 1) ;

    else if( node.anchorY == 0 ) node.y = - node.height * (index - 1) - node.height ;

    else node.y = - node.height * (index - 1) - node.height/2 ;

    this.showItemNodeList.push(node);

    this.showItemIndexList.push(index);

    this.refreshData(node,index - 1 );

}

private delItem(index)

{

    this.delPoolNode(this.showItemNodeList[index]);

    this.showItemIndexList.splice(index, 1);

    this.showItemNodeList.splice(index, 1);

}

private refreshData(node:cc.Node, index:number){

    if( index < 0 || index >= this.itemTotalNum ) return node.active = false ;

    let data = null ;

    if( this.dataList ) data = this.dataList[index];

    this.itemRenderHandler && this.itemRenderHandler.emit([node,data,index]);

    this.itemRenderFunction && this.itemRenderFunction(node,data,index);

}

private itemRenderFunction:Function = null ;



/**

 * 挂载之后,初始调用一次,每次刷新整个视图都需要调用这个接口。

 * scroll_com 属性,可实现原有的scrollView 的接口。

 * 刷新视图,默认滚动到最顶部。

 * @param data 可以是长度,也可以是数组。

 * @param itemRenderHandler 刷新item的异步回调,有三个参数(cc.node,object|null,number)。

 * @param itemPref 复用的item , item有监听事件,可能需要注销,在监听。预制体可挂载脚本,另做处理。

 * @param clearPool 复用不同的item,需要清理对象池。

 */

public refreshScrollView(data:Array<any>|number,itemRenderHandler:cc.Component.EventHandler|Function  = null ,itemPref:cc.Node = null , clearPool:boolean = false )

{

    if( itemPref ) this.itemPref = itemPref;

    this.init();

    if(  typeof data == "number" )

    {

        this.dataList = null ;

        this.itemTotalNum = data;

    }

    else

    {

        this.dataList = data ;

        this.itemTotalNum = data.length;

    }

    

    if( typeof itemRenderHandler == "function" ) this.itemRenderFunction = itemRenderHandler;

    else this.itemRenderHandler = itemRenderHandler ;

            

    this.maxItemIndex = this.itemTotalNum  ;

    this.content.height = ( this.maxItemIndex ) * this.itemPref.height ;

    // this.content.y = 0;

    this.scroll_com.scrollToTop(0.1);

    this.nowItemIndex = 0 ;

    this.showItemIndexList = [];

    for (let index = 0; index < this.showItemNodeList.length; index++) {

        const element = this.showItemNodeList[index];

        this.delPoolNode(element);

    }

    if( clearPool ) this.clearPoolNode();

    this.showItemNodeList = [];

    this.refreshItem(); 

}

/**

 * 

 * @param listViewParam 参数的规范,指传需要的参数,别的默认。具体的看实现。

 */

public refreshListView(listViewParam:ListViewParam)

{

    let data:any ;

    if(listViewParam.data.length == 0) data = listViewParam.itemLength ;

    else data = listViewParam.data ;

    if( listViewParam.itemLength != 0 ) this.itemTotalNum = listViewParam.itemLength ;

    let hndler:any ;

    if(listViewParam.itemRenderHandler == null) hndler = listViewParam.itemRenderFunction ;

    else hndler = listViewParam.itemRenderHandler ;

    this.refreshScrollView(data,hndler,listViewParam.itemPref,listViewParam.clearPool);

}

/**

 * 

 * @param data 可以是长度,也可以是数组。

 * @param itemRenderHandler 刷新item的异步回调,有三个参数(cc.node,object|null,number)。

 * @param self 是否需要绑定this指向

 */

public refreshHandler(data:Array<any>|number,itemRenderHandler:Function,self:cc.Component = null )

{

    if(self) this.itemRenderFunction = itemRenderHandler.bind(self);

    else this.itemRenderFunction = itemRenderHandler;

    this.refreshScrollView(data);

}

}

1赞

支持多行多列吗

多行多列,支持,但不够方便,懂的都懂,为什么支持,为什么不方便。哈哈。