FairyGUI笔记:列表(十九)

您所在的位置:网站首页 怎么获取list的索引 FairyGUI笔记:列表(十九)

FairyGUI笔记:列表(十九)

2023-08-12 14:07| 来源: 网络整理| 查看: 265

GList

列表对应的是GList.在FairyGUI中,列表的本质就是一个组件,GList也是从GComponent派生来的,所以你可以用GComponent的API直接访问列表能容,例如可以用GetChild或者GetChildAt访问列表内的项目;也可以用AddChild添加一个item。

当你对列表增删改后,列表是自动排列和刷新的,不需要调用任何API。自动排列时会根据列表的布局设置item的坐标、大小和深度,所以不要自行设置item的位置,也不要设置sortingOrder尝试去控制item的深度。除了一个例外,垂直布局的列表只会自动设置item的y坐标,如果你需要item有一个水平位移的效果,你仍然可以修改item的x值。水平布局的也是一样道理。

这个排列和刷新发生在本帧绘制之前,如果你希望立刻访问item的正确坐标,那么可以调用EnsureBoundsCorrect通知GList立刻重排。EnsureBoundsCorrect是一个友好的函数,你不用担心重复调用会有额外性能消耗。

在实际应用中,列表的内容通常被频繁的更新。典型的用法就是当接收到后台数据时,将列表清空,然后再重新添加所有项目。如果每次都创建和销毁UI对象,将消耗很大的CPU和内存。因此,GList内建了对象池。

使用对象池后的显示列表管理方法:

AddItemFromPool从池中取出(如果有)或者新建一个对象,添加到列表中。如果不使用参数,则使用列表的"项目资源"的设置;也可以指定一个URL,创建指定的对象。GetFromPool从池中取出(如果有)或者新建一个对象。RetrurnToPool将对象返回池里。RemoveChildToPool删除一个item,并将对象返回池里。RemoveChildrenToPool删除一个范围内的item,或者全部删除,并将删除的对象都返回池里

当应用到池时,我们就应该非常小心,一个不停增长的池那将是游戏的灾难,但如果不使用池,对游戏性能也会有影响。

错误示例1:

GObject obj = UIPackage.CreateObject(...); aList.AddChild(obj); aList.RemoveChildrenToPool();

添加对象时不使用池,但最后清除列表时却放到池里。这段代码持续运行,对象池将不断增大,可能造成内存溢出。 正确的做法:应从池中创建对象。将AddChild改成AddItemFromPool。

错误示例2:

for(int i=0;iitemRenderer = CC_CALLBACK_2(AClass::renderListItem, this);

最后直接设置列表中的项目总数,这样列表就会调整当前列表容器的对象数量,然后调用回调函数渲染item。

//创建100个对象,注意这里不能使用numChildren,numChildren是只读的。 aList.numItems = 100;

如果新设置的项目数小于当前的项目数,那么多出来的item将放回池里。

使用这种方式生成的列表,如果你需要更新某个item,自行调用RenderListItem(索引,GetChildAt(索引))就可以了。

scrollItemToViewOnClick

这是列表的一个选项,如果为true,当点击某个item时,如果这个item处于部分显示状态,那么列表将会自动滚动到整个item显示完整。 默认值是true。如果你的列表有超过列表视口大小的item,建议设置为false。

foldInvisibleItem

这是列表的一个选项,如果为true,但某个item不可见时(visible=false),列表不会为他留位置,也就是排版时会忽略这个item;如果为false,在列表会为这个item保留位置,显示效果就是一个空白的占位。默认值是false。

列表自动大小

严格来说,列表没有自动大小的功能。但GList提供了API根据item的数量设置列表的大小。当你填充完列表的数据后,可以调用GList.ResizeToFit,这样列表的大小就会修改为最适合的大小,容纳指定的item数量。如果不指定item数量,则列表扩展大小至显示所有item。

事件

点击列表内的某一个item触发事件:

//Unity/Cry, EventContext.data就是当前被点击的item对象 list.onClickItem.Add(onClickItem); //AS3, ItemEvent.itemObject就是当前被点击的对象 list.addEventListener(ItemEvent.CLICK, onClickItem); //Egret,ItemEvent.itemObject就是当前被点击的对象 list.addEventListener(ItemEvent.CLICK, this.onClickItem, this); //Laya, onClickItem方法的第一个参数就是当前被点击的对象 list.on(fairygui.Events.CLICK_ITEM, this, this.onClickItem); //Cocos2dx,EventContext.getData()就是当前被点击的item对象 list->addEventListener(UIEventType::ClickItem, CC_CALLBACK_1(AClass::onClickItem, this));

从上面的代码可以看出,事件回调里都可以方便的获得当前点击的对象。如果要获得索引,那么可以使用GetChildIndex。

虚拟列表

如果列表的item数量特别多时,例如几百上千,为每一条项目创建实体的显示对象将非常消耗时间和资源。FairyGUI的列表内置了虚拟机制,也就是它只为显示范围内的item创建实体对象,并通过动态设置数据的方式实现大容量列表。

启用虚拟列表有几个条件:

需要定义itemRenderer需要开启滚动。溢出处理不是滚动的列表不能开启虚拟。需要设置好列表的"项目资源"。可以在编辑器内设置,也可以调用GList.defaultItem设置。

满足条件后可以开启列表的虚拟功能:

aList.SetVirtual();

提示:虚拟功能只能开启,不能关闭。

虚拟列表的性能和itemRenderer的处理逻辑密切相关,你应该尽量简化这里面的逻辑,协程、IO、高密度计算这类操作不应该在这里出现,否者会出现卡顿。如果需要在itemRenderer里发起异步操作,切勿让异步操作保存ITEM实例,并且在回调中直接写该ITEM实例,正确的做法是让异步操作保存ITEM的索引,异步操作完成后,查询这个索引的ITEM是否有对应的显示对象,有则更新,如果没有,放弃更新

另外,itemRenderer里也不应该有new等会产生GC的操作,因为在滚动的过程中,itemRenderer调用频率会非常高。

在虚拟列表里,ITEM是服用的,当一个ITEM需要被刷新时,itemRenderer就会被调用,你无需关心这个调用的时机,也不能依赖这个时机。如果在itemRenderer你使用Add进行事件的侦听操作,绝不可以使用临时函数或者lamba表达式。

void EventCallback() { } EventCallback0 callback = EventCallback; void OnRenderItem(int index,GObect obj) { GButton btn = obj.asCom.GetChild("btn").asButton; //错误!,临时函数会造成添加多次回调。Lua里使用“function() end”类似。 btn.onClick.Add(()=>{}); //可以,同一个方法只会添加一次。但直接使用方法名会生成几十B的GC。 btn.onClick.Add(EventCallback); //正确,callback是缓存的代理实例,不会产生GC。 btn.onClick.Add(callback); //正确,使用Set设置可以保证不会重复添加。 btn.onClick.Set(callback); //错误!,不能对ITEM使用onClick.Set,你需要用GList.onClickItem obj.onClick.Set(EventCallback); }

AS3/Starling/Egret/Laya参考:

// private function EventCallback(evt:Event):void { } private function onRenderItem(index:int, obj:GObject):void { var btn:GButton = obj.asCom.getChild("btn").asButton; //错误,这里不应该使用临时函数 btn.addClickListener(function():void {}); //正确,同一个方法只会添加一次 btn.addClickListener(EventCallback); }

在虚拟列表中,显示对象和item的数量在数量上和顺序上是不一致的,item的数量可以通过numItems获得,而显示对象的数量可以由组件的API numChildren获得。

在虚拟列表中,需要注意item索引和显示对象索引的区分。通过selectedIndex获得的值是item的索引,而非显示对象的索引。AddSeledtion/RemoveSelection等API同样需要的是item的索引。项目索引和对象索引的转换可以通过以下两种方法完成:

//转换项目索引为显示对象索引。 int childIndex = aList.ItemIndexToChildIndex(1); //转换显示对象索引为项目索引。 int itemIndex = aList.ChildIndexToItemIndex(1);

使用虚拟列表时,我们很少会需要访问屏外对象。如果你确实需要获得列表中指定索引的某一个项目的显示对象,例如第500个,因为当前这个item是不在视口的,对于虚拟列表,不在视口的对象是没有对应的显示对象的,那么你需要先让列表滚动到目标位置。例如:

//这里要注意,因为我们要立即访问新滚动位置的对象,所以第二个参数scrollItToView不能为true,即不使用动画效果 aList.ScrollToView(500); //转换到显示对象索引 int index = aList.ItemIndexToChildIndex(500); //这就是你要的第500个对象 GObject obj = aList.GetChildAt(index);

虚拟列表的本质是数据和渲染分离,经常有人问怎样删除、或者修改虚拟列表的项目,答案就是先修改你的数据,然后刷新列表就可以了,不需要获得某个item对象来处理。 刷新虚拟列表的方式有两种:

使用numItems重新设置数量GList.RefreshVirtualList。

不允许使用AddChild或RemoveChild对虚拟列表增删对象。如果要清空列表,必须要通过设置numItems=0,而不是RemoveChildren。

虚拟列表支持可变大小的item,可以通过两种方式动态改变item的大小:

在itemRenderer的内部使用width、height或SetSize改变item的大小。item建立对内部元件的关联,例如item建立了一个对内部某个可变高度文本的高高关联,这样当文本改变时,item的高度自动改变。

除这两种方式外,不可以通过其他方式改变item大小,否则虚拟列表排列会错乱。

虚拟列表支持不同类型的item混合。首先为列表定义一个回调函数,例如

//根据索引的不同,返回不同的资源URL string GetListItemResource(int index) { Message msg = _messages[index]; if (msg.fromMe) return "ui://Emoji/chatRight"; else return "ui://Emoji/chatLeft"; }

然后设置这个函数为列表的item提供者:

//Unity/Cry aList.itemProvider = GetListItemResource; //AS3 aList.itemProvider = GetListItemResource; //Egret aList.itemProvider = GetListItemResource; aList.callbackThisObj = this; //Laya。(注意,最后一个参数必须为false!) aList.itemProvider = Handler.create(this, this.GetListItemResource, null, false); //Cocos2dx aList->itemProvider = CC_CALLBACK_1(AClass::getListItemResource, this);

对于横向流动、竖向流动和分页的列表,与非虚拟列表具有流动特性不同,虚拟列表每行或每列的item个数都是固定的。列表在初始化时会创建一个默认的item用于测算这个数量。 如果你仍然需要每行或每列不等item数量的排版,且必须使用虚拟化,那么可以插入一些用于占位的空组件或者空图形,并根据实际需要设置他们的宽度,从而实现那种排版效果。

循环列表

循环列表是指首尾相连的列表,循环列表必须是虚拟列表。启用循环列表的方法为:

aList.SetVirtualAndLoop()。

循环列表只支持单行或者单列的布局,不支持流动布局和分页布局。 因为循环列表是首尾相连的,指定一个item索引可能出现在不同的位置,所以需要指定滚定位置时,尽量避免使用item索引。例如,如果需要循环列表左/上滚一格或者右/下滚一格,最好的办法就是调用ScrollPane的API:ScrollLeft/ScrollRight/ScrollUp/ScrollDown 循环列表的特性与虚拟列表一致,在此不再赘述。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3