Laya FairyGui系列八 GList

列表(GList)

游戲開發(fā)過程中經(jīng)常會用到列表組件,比如我們做排行榜,服務(wù)器列表等。FGUI的列表能實現(xiàn)很多種復(fù)雜效果,相比較會比Laya原生的列表要強大。FGUI的虛擬列表還可以很好的處理數(shù)據(jù)量比較大的列表。

列表的基本用法很簡單,還是一樣點擊左邊工具欄上的列表按鈕創(chuàng)建一個列表,列表很多時候里面顯示的Iiem是差不多的,就是里面的圖標換一下或者文本內(nèi)容換一下啦,前面說GButton的時候也又提到過,列表的item使用的是GButton。這里設(shè)置列表的Item組件。


GList_0.png
  • 渲染順序(列表是特殊容器,Item是列表的子節(jié)點,多個Item就有渲染順序的問題,誰顯示再前面誰顯示再后面):

    • 升序 按照Item索引升序排列
    • 降序 按照Item索引降序排列
    • 拱形 自定義個顯示再最前面的item,如果指定的是5,那么列表中第6個就顯示在最前面。


      GList_3.png
  • 邊緣 設(shè)置Item在列表中的位置

  • 觸摸滾動效果 可以關(guān)閉列表滾動功能

為什么會是使用GButton呢,比如我們再做一個背包界面,每個物品都是一個背景,Icon,文本組成,這不是和GButton正好一樣么,背包里面的物品需要點擊GButton也支持。當然了,你一定不想用GButton,一定要用其他的也是可以的。
關(guān)于列表的屬性有很多,我就不一一介紹了還是看官網(wǎng)

列表的使用

  • 添加列表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);

當你對列表進行增加刪除或者修改操作后,列表的排列和刷新是自動的,不需要調(diào)用任何API。

  • 獲取列表中Item個數(shù)
// 獲取列表中Item個數(shù)
console.log('numChildren = ' + testList.numChildren); //numChildren = 7
//獲取列表中數(shù)據(jù)個數(shù)
console.log('numItems =' + testList.numItems); // numItems =7

在列表中Glist.numChildren永遠是等于Glist.numItems的,后面說到的虛擬列表就不一定了。

  • 點擊列表內(nèi)的某一個item觸發(fā)事件
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');
}

我們在開發(fā)排行榜時列表的內(nèi)容會頻繁的的更新,一般時從后臺接受到數(shù)據(jù)后先清空列表,然后重新添加新的項目。但是會對象頻繁的創(chuàng)建銷毀會消耗很多的內(nèi)存和CPU。因此列表內(nèi)建了對象池解決這個問題。
使用對象池管理item的相關(guān)方法:

//從對象池中添加一個item到列表中,這里我們不用管對象池中有沒有item,如果對象池時空的會fgui會自動幫我們創(chuàng)建
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);
// 將指定范圍內(nèi)的Item從列表中移除并放回對象池,也可以移除全部(如果結(jié)束位置的索引小于0或者大于Item數(shù)量則移除全部)
testList.removeChildrenToPool(0,3);

虛擬列表

前面說到了FGUI可以很好的處理數(shù)據(jù)量很大的情況,試想一下玩家在第一次打開排行榜,這時候排行榜中有100條排名信息,如果我們不用循環(huán)的方式怎么將數(shù)據(jù)顯示到item上呢,列表會有一個item渲染的回調(diào)函數(shù),每一個item再被創(chuàng)建時都會將item對象傳到回調(diào)函數(shù)中,我們可以再回調(diào)函數(shù)中進行數(shù)據(jù)和UI的綁定。

//設(shè)置列表的渲染回調(diào)
testList.itemRenderer = Laya.Handler.create(this,this.renderItem,null,false)
//在設(shè)置列表要顯示的item數(shù)量(注意,這里一定要先設(shè)置渲染回調(diào)再設(shè)置item數(shù)量)
testList.numItems= 100;

private renderItem(index:number,item:fairygui.GComponent){
    //可以再這里將排行榜數(shù)據(jù)顯示到item中
    const itemButton = item.asButton;
    itemButton.title = '第' + index + '個';
    console.log('item個數(shù):' + item.parent._children.length);
}
GList_4.png

通過打印看到創(chuàng)建了100個item。
那么就算使用對象池也需要創(chuàng)建100個Item對象,這個開銷也是很可怕的,關(guān)鍵是這100個item并不是每一個會都被玩家看到,可能玩家只會關(guān)心排名靠前的幾個,那么后面的很多創(chuàng)建出來就是浪費的。
虛擬列表的處理方法是不管有多少個數(shù)據(jù),始終只會創(chuàng)建出能在列表中顯示出來的數(shù)量,在玩家滾動列表時動態(tài)設(shè)置數(shù)據(jù)。
說了這么多,其實我們只要再上面代碼中設(shè)置渲染回調(diào)函數(shù)設(shè)置之前加上一句話就行了。

//開啟虛擬列表
testList.setVirtual();
GList_5.png

再看下打印只創(chuàng)建了6個Item對象。只有6個Item是怎么顯示100條排行榜信息呢。剛才的回調(diào)函數(shù)里面還有一個參數(shù)index這個時候就要用到了。
如果我們100條數(shù)據(jù)保存再一個數(shù)組里面。這6條item也是保存再數(shù)組里面,這里就有兩個索引了

  • ItemIndex 對應(yīng)著100條排行信息的索引
  • ChildIndex 對應(yīng)著6個item對象的索引
    這兩個索引有著一個轉(zhuǎn)換接口:
    //根據(jù)itemIndex獲取ChildIndex
    const childIndex = testList.itemIndexToChildIndex(index);
    //根據(jù)ChildIndex獲取itemIndex
    const itemIndex = testList.childIndexToItemIndex(childIndex);

上面說到的回調(diào)函數(shù)中的index就是ItemIndex。然后根據(jù)這個index就可以從100條數(shù)據(jù)中取出應(yīng)該現(xiàn)在再這個item上的數(shù)據(jù)了。
有些時候可能會有這個需求,玩家打開排行榜之后列表需要直接定位到自己的排名信息。

//假設(shè)玩家自得排名再第80名
const itemindex = 79
//滾動到指定的item,這里的參數(shù)應(yīng)是itemIndex
testList.scrollToView(itemindex);
const childIndex = testList.itemIndexToChildIndex(itemindex);
// 這就是玩家自己排名信息得item
const item = testList.getChildAt(childIndex);

某些情況下可能不想讓整個item接受點擊事件,會再item內(nèi)部放一個按鈕讓這個按鈕響應(yīng)點擊事件,那么上面說到得按鈕點擊事件可能就不行了,這時候可以在渲染函數(shù)中設(shè)置點擊事件。但是覺得不可以用匿名函數(shù)或者lamba表達式。否則會造成點擊事件多次觸發(fā)。

    //這里因為onClick事件是從Laya中觸發(fā),
    //在點擊的回調(diào)函數(shù)中不能取到GButton對象,只能夠取到GButton._displayObject。
    //所以這里將GButton對象作為參數(shù)傳遞到回調(diào)函數(shù)中
    itemButton.onClick(this,this.onClickItem,[itemButton]);

        //這樣設(shè)置回調(diào)可能會導致同一個item下會存在多個匿名函數(shù)的點擊響應(yīng)函數(shù),會觸發(fā)多次回調(diào)函數(shù)
    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');
    }

注:

  • 虛擬列表一旦開啟就不能關(guān)閉。
  • 設(shè)置列表或者虛擬列表的numItems之前需要先設(shè)置渲染函數(shù)
  • 虛擬列表一定需要設(shè)置渲染函數(shù)
  • 虛擬列表的溢出處理需要設(shè)置成滾動,否則不能開啟虛擬列表
    GList_6.png
  • 虛擬列表只能渲染設(shè)置好的item組件,可以在編輯器中設(shè)置,也可以通過GList.defaultItem設(shè)置。
  • 虛擬列表不可以自己管理item,即不可以調(diào)用AddChild或者RemoveChild。只能通過numItems改變item的數(shù)量
  • 如果想讓虛擬列表的首尾相連可以使用GList.setVirtualAndLoop開啟
  • 如果需要動態(tài)修改item的大小,可以在渲染函數(shù)中設(shè)置item的大小或者在設(shè)置item大小后調(diào)用GList.RefreshVirtualList刷新虛擬列表。
  • 獲取距離最近的錨點的item的錨點的坐標
console.log(testList.getSnappingPosition(Laya.stage.mouseX,Laya.stage.mouseY));
  • 獲取當前處于顯示區(qū)域中的第一個item的索引
console.log(testList.getFirstChildInView());
  • 調(diào)整列表的大小到能夠顯示全部item的尺寸
testList.resizeToFit();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容