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 <any> = []; /**应创建的实例数量 */ private spawnCount : number = 0 ; /**存放列表项实例的数组 */ private itemList : Array < cc . Node > = []; /**item的高度 */ private itemHeight : number = 0 ; /**item的宽度 */ private itemWidth : number = 0 ; /**存放不再使用中的列表项 */ private itemPool : Array < cc . Node > = []; //======================= 计算参数 ========================== /**距离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 <any> ) { 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 < this . spawnCount ; i ++) { let item : cc . Node ; //需要显示的数据索引在数据范围内,则item实例显示出来 if ( i + startIndex < this . itemDataList . length ) { if ( this . itemList [ i ] == null ) { item = this . getItem (); this . itemList . push ( item ); item . parent = this . content ; } else { item = this . itemList [ i ]; } //需要显示的数据索引超过了数据范围,则item实例隐藏起来 } else { //item实例数量 > 需要显示的数据量 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 . updateInterval ) { return ; } this . updateTimer = 0 ; this . bScrolling = false ; this . updateFun (); } onScrolling () { this . bScrolling = true ; } /**垂直排列 */ private updateV () { let items = this . itemList ; let item ; let bufferZone = this . halfScrollView ; let isUp = this . scrollView . content . y > this . lastContentPosY ; let offset = ( this . itemHeight + this . spaceY ) * items . length ; for ( let i = 0 ; i < items . length ; i ++) { item = items [ i ]; let viewPos = this . getPositionInView ( item ); if ( isUp ) { //item上滑时,超出了scrollView上边界,将item移动到下方复用,item移动到下方的位置必须不超过content的下边界 if ( viewPos . y > 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 < items . length ; i ++) { item = items [ i ]; let viewPos = this . getPositionInView ( item ); if ( isRight ) { //item右滑时,超出了scrollView右边界,将item移动到左方复用,item移动到左方的位置必须不超过content的左边界 if ( viewPos . x > 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 . content . width ) { 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 ; } } } this . lastContentPosX = this . scrollView . content . x ; } /**网格垂直排列 */ private updateGrid_V () { let items = this . itemList ; let item : cc . Node ; let bufferZone = this . halfScrollView ; let isUp = this . scrollView . content . y > this . lastContentPosY ; let offset = ( this . itemHeight + this . spaceY ) * ( this . spawnCount / this . gridCol ); for ( let i = 0 ; i < items . length ; i ++) { item = items [ i ]; let viewPos = this . getPositionInView ( item ); if ( isUp ) { //item上滑时,超出了scrollView上边界,将item移动到下方复用,item移动到下方的位置必须不超过content的下边界 if ( viewPos . y > 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 < items . length ; i ++) { item = items [ i ]; let viewPos = this . getPositionInView ( item ); if ( isRight ) { //item右滑时,超出了scrollView右边界,将item移动到左方复用,item移动到左方的位置必须不超过content的左边界 if ( viewPos . x > 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 < this . content . width ) { 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 ; } } } } this . lastContentPosX = this . scrollView . content . x ; } /**获取item在scrollView的局部坐标 */ private getPositionInView ( item ) { let worldPos = item . parent . convertToWorldSpaceAR ( item . position ); let viewPos = this . scrollView . node . convertToNodeSpaceAR ( worldPos ); return viewPos ; } /**获取列表数据 */ public getListData () { return this . itemDataList ; } /** * 增加一项数据到列表的末尾 * @param data 数据 */ public addItem ( data : any ) { this . itemDataList . push ( data ); this . updateContent (); } /** * 增加一项数据到列表指定位置 * @param index 位置,0表示第1项 * @param data 数据 */ public addItemAt ( index : number , data : any ) { if ( this . itemDataList [ index ] != null || this . itemDataList . length == index ) { this . itemDataList . splice ( index , 1 , data ); this . updateContent (); } } /** * 删除一项数据 * @param index 删除项的位置 ,0表示第1项 */ public deleteItem ( index : number ) { if ( this . itemDataList [ index ] != null ) { this . itemDataList . splice ( index , 1 ); this . updateContent (); } } /** * 改变一项数据 * @param index 位置,0表示第1项 * @param data 替换的数据 */ public changeItem ( index : number , data : any ) { if ( this . itemDataList [ index ] != null ) { this . itemDataList [ index ] = data ; this . updateContent (); } } /**获取第一个Item的位置 */ private updateContent () { //显示列表实例为0个 if ( this . itemList . length == 0 ) { this . countListParam (); this . createList ( 0 , new cc . Vec2 ( 0 , 0 )); //显示列表的实例不为0个,则需要重新排列item实例数组 } else { if ( this . type == ListType . Vertical ) { this . itemList . sort (( a : any , b : any ) => { 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 < len ; i ++) { if ( cc . isValid ( this . itemList [ i ], true )) { this . itemList [ i ]. destroy (); } } this . itemList . length = 0 ; //清理对象池 len = this . itemPool . length ; for ( let i = 0 ; i < len ; i ++) { if ( cc . isValid ( this . itemPool [ i ], true )) { this . itemPool [ i ]. destroy (); } } this . itemPool . length = 0 ; //清理列表数据 this . itemDataList . length = 0 ; } }
以上就是如何在CocosCreator中做一个List的详细内容,更多关于CocosCreator List的资料请关注服务器之家其它相关文章!
原文链接:https://www.cnblogs.com/gamedaybyday/p/13270209.html
查看更多关于如何在CocosCreator中做一个List的详细内容...
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did63460