列表(GList)
游戏开发过程中经常会用到列表组件,比如我们做排行榜,服务器列表等。FGUI的列表能实现很多种复杂效果,相比较会比Laya原生的列表要强大。FGUI的虚拟列表还可以很好的处理数据量比较大的列表。
列表的基本用法很简单,还是一样点击左边工具栏上的列表按钮创建一个列表,列表很多时候里面显示的Iiem是差不多的,就是里面的图标换一下或者文本内容换一下啦,前面说GButton的时候也又提到过,列表的item使用的是GButton。这里设置列表的Item组件。
-
渲染顺序(列表是特殊容器,Item是列表的子节点,多个Item就有渲染顺序的问题,谁显示再前面谁显示再后面):
- 升序 按照Item索引升序排列
- 降序 按照Item索引降序排列
-
拱形 自定义个显示再最前面的item,如果指定的是5,那么列表中第6个就显示在最前面。
边缘 设置Item在列表中的位置
触摸滚动效果 可以关闭列表滚动功能
为什么会是使用GButton呢,比如我们再做一个背包界面,每个物品都是一个背景,Icon,文本组成,这不是和GButton正好一样么,背包里面的物品需要点击GButton也支持。当然了,你一定不想用GButton,一定要用其他的也是可以的。
关于列表的属性有很多,我就不一一介绍了还是看官网。
列表的使用
- 添加列表Item
const listItem = fairygui.UIPackage.createObject("Package1",'listItem2').asButton;
listItem.title = "第6个";
testList.addChild(listItem);
- 通过FGUI URL添加列表Item
const listItem2 = testList.addItem("ui://Package1/listItem2").asButton;
listItem2.title = "第7个";
- 删除列表Item
testList.removeChild(listItem2);
testList.removeChildAt(0);
当你对列表进行增加删除或者修改操作后,列表的排列和刷新是自动的,不需要调用任何API。
- 获取列表中Item个数
// 获取列表中Item个数
console.log('numChildren = ' + testList.numChildren); //numChildren = 7
//获取列表中数据个数
console.log('numItems =' + testList.numItems); // numItems =7
在列表中Glist.numChildren永远是等于Glist.numItems的,后面说到的虚拟列表就不一定了。
- 点击列表内的某一个item触发事件
testList.on(fairygui.Events.CLICK_ITEM,this,this.onClickItem)
private onClickItem(item:fairygui.GComponent,event:any){
const itemButton = item.asButton;
console.log('点击了' + itemButton.title + 'Button');
}
我们在开发排行榜时列表的内容会频繁的的更新,一般时从后台接受到数据后先清空列表,然后重新添加新的项目。但是会对象频繁的创建销毁会消耗很多的内存和CPU。因此列表内建了对象池解决这个问题。
使用对象池管理item的相关方法:
//从对象池中添加一个item到列表中,这里我们不用管对象池中有没有item,如果对象池时空的会fgui会自动帮我们创建
const listItem0 = testList.addItemFromPool("ui://Package1/listItem2").asButton;
listItem0.title = '第1个';
const listItem1 = testList.addItemFromPool("ui://Package1/listItem2").asButton;
listItem1.title = '第2个';
const listItem2 = testList.addItemFromPool("ui://Package1/listItem2").asButton;
listItem2.title = '第3个';
const listItem3 = testList.addItemFromPool("ui://Package1/listItem2").asButton;
listItem3.title = '第4个';
// 将Item从列表中移除并放回对象池
testList.removeChildToPool(listItem1);
// 将指定位置的Item从列表中移除并放回对象池
testList.removeChildToPoolAt(0);
// 将指定范围内的Item从列表中移除并放回对象池,也可以移除全部(如果结束位置的索引小于0或者大于Item数量则移除全部)
testList.removeChildrenToPool(0,3);
虚拟列表
前面说到了FGUI可以很好的处理数据量很大的情况,试想一下玩家在第一次打开排行榜,这时候排行榜中有100条排名信息,如果我们不用循环的方式怎么将数据显示到item上呢,列表会有一个item渲染的回调函数,每一个item再被创建时都会将item对象传到回调函数中,我们可以再回调函数中进行数据和UI的绑定。
//设置列表的渲染回调
testList.itemRenderer = Laya.Handler.create(this,this.renderItem,null,false)
//在设置列表要显示的item数量(注意,这里一定要先设置渲染回调再设置item数量)
testList.numItems= 100;
private renderItem(index:number,item:fairygui.GComponent){
//可以再这里将排行榜数据显示到item中
const itemButton = item.asButton;
itemButton.title = '第' + index + '个';
console.log('item个数:' + item.parent._children.length);
}
通过打印看到创建了100个item。
那么就算使用对象池也需要创建100个Item对象,这个开销也是很可怕的,关键是这100个item并不是每一个会都被玩家看到,可能玩家只会关心排名靠前的几个,那么后面的很多创建出来就是浪费的。
虚拟列表的处理方法是不管有多少个数据,始终只会创建出能在列表中显示出来的数量,在玩家滚动列表时动态设置数据。
说了这么多,其实我们只要再上面代码中设置渲染回调函数设置之前加上一句话就行了。
//开启虚拟列表
testList.setVirtual();
再看下打印只创建了6个Item对象。只有6个Item是怎么显示100条排行榜信息呢。刚才的回调函数里面还有一个参数index这个时候就要用到了。
如果我们100条数据保存再一个数组里面。这6条item也是保存再数组里面,这里就有两个索引了
- ItemIndex 对应着100条排行信息的索引
- ChildIndex 对应着6个item对象的索引
这两个索引有着一个转换接口:
//根据itemIndex获取ChildIndex
const childIndex = testList.itemIndexToChildIndex(index);
//根据ChildIndex获取itemIndex
const itemIndex = testList.childIndexToItemIndex(childIndex);
上面说到的回调函数中的index就是ItemIndex。然后根据这个index就可以从100条数据中取出应该现在再这个item上的数据了。
有些时候可能会有这个需求,玩家打开排行榜之后列表需要直接定位到自己的排名信息。
//假设玩家自得排名再第80名
const itemindex = 79
//滚动到指定的item,这里的参数应是itemIndex
testList.scrollToView(itemindex);
const childIndex = testList.itemIndexToChildIndex(itemindex);
// 这就是玩家自己排名信息得item
const item = testList.getChildAt(childIndex);
某些情况下可能不想让整个item接受点击事件,会再item内部放一个按钮让这个按钮响应点击事件,那么上面说到得按钮点击事件可能就不行了,这时候可以在渲染函数中设置点击事件。但是觉得不可以用匿名函数或者lamba表达式。否则会造成点击事件多次触发。
//这里因为onClick事件是从Laya中触发,
//在点击的回调函数中不能取到GButton对象,只能够取到GButton._displayObject。
//所以这里将GButton对象作为参数传递到回调函数中
itemButton.onClick(this,this.onClickItem,[itemButton]);
//这样设置回调可能会导致同一个item下会存在多个匿名函数的点击响应函数,会触发多次回调函数
itemButton.onClick(this,(item:fairygui.GButton,event:any)=>{
},[itemButton]);
private onClickItem(item:fairygui.GButton,event:any){
const itemButton = item.asButton;
console.log('点击了' + itemButton.title + 'Button');
}
注:
- 虚拟列表一旦开启就不能关闭。
- 设置列表或者虚拟列表的numItems之前需要先设置渲染函数
- 虚拟列表一定需要设置渲染函数
-
虚拟列表的溢出处理需要设置成滚动,否则不能开启虚拟列表
- 虚拟列表只能渲染设置好的item组件,可以在编辑器中设置,也可以通过GList.defaultItem设置。
- 虚拟列表不可以自己管理item,即不可以调用AddChild或者RemoveChild。只能通过numItems改变item的数量
- 如果想让虚拟列表的首尾相连可以使用GList.setVirtualAndLoop开启
- 如果需要动态修改item的大小,可以在渲染函数中设置item的大小或者在设置item大小后调用GList.RefreshVirtualList刷新虚拟列表。
- 获取距离最近的锚点的item的锚点的坐标
console.log(testList.getSnappingPosition(Laya.stage.mouseX,Laya.stage.mouseY));
- 获取当前处于显示区域中的第一个item的索引
console.log(testList.getFirstChildInView());
- 调整列表的大小到能够显示全部item的尺寸
testList.resizeToFit();