List

Lists are a special extension of components. Click on the side toolbarButton to generate a list.

List properties

Click a list on the stage, and the property bar on the right shows the properties of the list:

ClickAfter the secondary interface pops up:

Click “Edit List” to display the dialog box:

Click Add to add an item specified by “Project Resources”. If your list requires multiple resources to be mixed, you can add an item first, and then drag components from the library to the “Resources” column to replace the default resources.

The title, icon, and name attributes of the item can be edited in the table. For other attributes, you can select the item and modify it in the inspector on the right.

“Automatically clear when publishing” means that the list data edited here will not be included in the publishing results when it is finally published, that is, the list data is only used for editor preview purposes.

GList

Manage list content

The corresponding type of the list is GList. In FairyGUI, the essence of a list is a component. GList is also derived from GComponent, so you can use GComponent’s API to directly access the contents of the list. For example, you can use GetChild or GetChildAt to access the items in the list; you can also use AddChild to add An item. This part of the API can refer to GComponentDisplay list management

When you add, delete, or modify a list, the list is automatically sorted and refreshed without calling any API. During the automatic arrangement, the coordinates, size, and depth of the items are set according to the layout of the list, so do not set the position of the items yourself, or set the sorting order to try to control the depth of the items. With one exception, the vertical layout list will only set the item’s y coordinate automatically. If you need the item to have a horizontal displacement effect, you can still modify the item’s x value. The same goes for horizontal layout.

This permutation and refresh occurs before drawing this frame. If you want to immediately access the correct coordinates of the item, you can callEnsureBoundsCorrectTell GList to reorder immediately. EnsureBoundsCorrect is a friendly function, you don’t need to worry about the extra performance cost of repeated calls.

In practical applications, the contents of the list are frequently updated. The typical usage is to clear the list when background data is received, and then add all items again. If you create and destroy UI objects every time, it will consume a lot of CPU and memory. Therefore, GList has a built-in object pool.

Management method of display list after using object pool:

You should be smart enough to know that AddItemFromPool = GetFromPool + AddChild, RemoveChildToPool = RemoveChild + ReturnToPool.

When applied to the pool, we should be very careful. A constantly growing pool will be a disaster for the game, but if the pool is not used, it will also affect the performance of the game.
Here are some examples of incorrect usage:

Error example 1:

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

The pool is not used when adding objects, but is placed in the pool when the list is finally cleared. This code continues to run, the object pool will continue to grow, which may cause memory overflow.
Correct way: Objects should be created from the pool. Change AddChild to AddItemFromPool.

Error example 2:

for(int i=0;i<10;i++)
aList.AddItemFromPool();
aList.RemoveChildren();

10 items were added here, but their references were not saved when they were removed, and they were not put back in the pool, which caused a memory leak. Change aList.RemoveChildren to aList.RemoveChildrenToPool ();

Removal and destruction are two different things. When you remove an item from the list, it should be destroyed if it is no longer used; if you still need it, please save its reference. But if you put it in the pool, do not destroy the item anymore

Modifying the list with a callback function

When adding a large number of items, in addition to the circular method AddChild or AddItemFromPool, you can also use another callback method. First define a callback function for the list, for example

void RenderListItem(int index, GObject obj)
{
GButton button = obj.asButton;
button.title = ""+index;
}

Then set this function to the list rendering function:

// Unity / Cry / Mono Game
    aList.itemRenderer = RenderListItem;
    
    // AS3
    aList.itemRenderer = renderListItem;
    
    // Egret
    aList.itemRenderer = renderListItem;
    aList.callbackThisObj = this;
    
    // Laya. (Note that the last parameter must be false!)
    aList.itemRenderer = Handler.create (this, this.renderListItem, null, false);
    // Cocos2dx
    aList-> itemRenderer = CC_CALLBACK_2 (AClass :: renderListItem, this);
    // CocosCreator
    aList.itemRenderer = this.renderListItem.bind (this);

Finally, directly set the total number of items in the list, so that the list will adjust the number of objects in the current list container, and then call the callback function to render the item.

// Create 100 objects. Note that numChildren cannot be used here. NumChildren is read-only.
    
    aList.numItems = 10;

If the number of newly set items is less than the current number of items, the extra items will be put back into the pool.

Using the list generated in this way, if you need to update an item, you can call RenderListItem (Index, GetChildAt (Index)) by yourself.

List autosize

Strictly speaking, lists don’t have auto-size capabilities. But GList provides API to set the list size according to the number of items. After you have filled the data of the list, you can call GList.ResizeToFit, so that the size of the list will be modified to the most suitable size to accommodate the specified number of items. If you do not specify the number of items, the list is expanded to show all items.

Event

Click on an item in the list to trigger the event:

// Unity / Cry / MonoGame, EventContext.data is the item object that is currently clicked
    list.onClickItem.Add (onClickItem);
    
    // AS3, ItemEvent.itemObject is the currently clicked object
    list.addEventListener (ItemEvent.CLICK, onClickItem);
    
    // Egret, ItemEvent.itemObject is the currently clicked object
    list.addEventListener (ItemEvent.CLICK, this.onClickItem, this);
    
    // Laya, the first parameter of the onClickItem method is the currently clicked object
    list.on (fairygui.Events.CLICK_ITEM, this, this.onClickItem);
    // Cocos2dx, EventContext.getData () is the item object that is currently clicked
    list-> addEventListener (UIEventType :: ClickItem, CC_CALLBACK_1 (AClass :: onClickItem, this));
    // CocosCreator, the first parameter of onClickItem is the currently clicked object, and the optional second object is fgui.Event.
    list.on (fgui.Event.CLICK_ITEM, this.onClickItem, this);

As can be seen from the above code, the current click object can be easily obtained in the event callback. If you want to get the index, you can use GetChildIndex.

Virtual list

If the number of items in the list is particularly large, such as hundreds or thousands, creating a display object of an entity for each item will consume time and resources. FairyGUI’s list has a built-in virtual mechanism, that is, it only creates entity objects for items within the display range, and implements large-capacity lists by dynamically setting data.

There are several conditions for enabling virtual lists:

After the conditions are met, the virtual function of the list can be turned on:

aList.SetVirtual ();

Tip: The virtual function can only be turned on and cannot be turned off.

The performance of the virtual list is closely related to the processing logic of itemRenderer. You should try to simplify the logic inside. Coroutines, IO, and high-density calculations should not appear here. Otherwise, there will be stuttering. If you need to initiate an asynchronous operation in itemRenderer, do not let the asynchronous operation save the ITEM instance, and modify the ITEM instance directly in the callback. The correct method is to let the asynchronous operation save the ITEM index. After the asynchronous operation is completed, check whether the ITEM of this index is checked. If there is a corresponding display object, update if there is one. If not, discard the update.
In addition, the itemRenderer should not have operations such as new that will cause GC, because the itemRenderer will be called very frequently during the scrolling process.

In the virtual list, ITEM is reused. When an ITEM needs to be refreshed, the itemRenderer will be called. You don’t need to care about the timing of this call, nor can you rely on this timing. Please note that if you use Add to listen for events in itemRenderer,Never use temporary functions or lamba expressions。 The following example illustrates this.

C # reference:

void EventCallback()
{
}
EventCallback0 callback = EventCallback;
void OnRenderItem(int index, GObject obj)
{
GButton btn = obj.asCom.GetChild("btn").asButton;
//错误! Temporary functions will cause multiple callbacks to be added. Lua uses "function () end" similarly.
        btn.onClick.Add (() => {});
        // Yes, the same method will only be added once. But using the method name directly will generate tens of B of GC.
        btn.onClick.Add (EventCallback);
        // Correct, callback is a cached proxy instance and does not generate GC.
        btn.onClick.Add (callback);
        // Correct, use Set to ensure that it will not be added repeatedly.
        btn.onClick.Set (callback);
        //error! , You cannot use onClick.Set for ITEM, you need to use GList.onClickItem
        obj.onClick.Set (EventCallback);
    }

AS3 / Starling / Egret / Laya Reference:

//
    private function EventCallback (evt: Event): void
    {
    }
    private function onRenderItem (index: int, obj: GObject): void
    {
        var btn: GButton = obj.asCom.getChild ("btn"). asButton;
        // Error, temporary functions should not be used here
        btn.addClickListener (function (): void (});
        // correct, the same method will only be added once
        btn.addClickListener (EventCallback);
    }

In the virtual list, the number and order of display objects and items are inconsistent. The number of items can be obtained through numItems, and the number of display objects can be obtained through the component’s API numChildren.

In the virtual list, it is necessary to pay attention to the distinction between the item index and the display object index. The value obtained by selectedIndex is the index of the item, not the index of the display object. AddSelection / RemoveSelection and other APIs also need the index of the item. The conversion of the item index and the object index can be completed by the following two methods:

// Convert the item index to the display object index.
    int childIndex = aList.ItemIndexToChildIndex (1);
    
    // Convert the display object index to the item index.
    int itemIndex = aList.ChildIndexToItemIndex (1);

When using virtual lists, we rarely need to access off-screen objects. If you really need to get the display object of an item at the specified index in the list, such as the 500th, because the current item is not in the viewport, for a virtual list, there is no corresponding display object for the object not in the viewport, You need to scroll the list to the target position first. E.g:

// Note here, because we want to access the object at the new scroll position immediately, the second parameter scrollItToView cannot be true, that is, no animation effect is used
    aList.ScrollToView (500);
    
    // Convert to display object index
    int index = aList.ItemIndexToChildIndex (500);
    
    // This is the 500th object you want
    GObject obj = aList.GetChildAt (index);

The essence of a virtual list is the separation of data and rendering. People often ask how to delete or modify the items of the virtual list. The answer is to modify your data first, then refresh the list.
There are two ways to refresh the virtual list:

AddChild or RemoveChild is not allowed to add or delete objects from the virtual list. If you want to clear the list, you must set numItems = 0 instead of RemoveChildren.

The virtual list supports variable-size items. There are two ways to dynamically change the size of an item:

In addition to these two methods, you cannot change the item size in other ways than itemRenderer, otherwise the virtual list will be arranged in disorder. But you can force the itemRenderer to be triggered by calling RefreshVirtualList.

A virtual list supports a mix of different types of items. First define a callback function for the list, for example

// Returns different resource URLs based on different indexes
    string GetListItemResource (int index)
    {
        Message msg = _messages [index];
        if (msg.fromMe)
            return "ui://Emoji/chatRight";
        else
            return "ui://Emoji/chatLeft";
    }

Then set this function as the item provider for the list:

//Unity/Cry
aList.itemProvider = GetListItemResource;
//AS3
aList.itemProvider = getListItemResource;
//Egret
aList.itemProvider = getListItemResource;
aList.callbackThisObj = this;
//Laya。 (Note that the last parameter must be false!)
    aList.itemProvider = Handler.create (this, this.getListItemResource, null, false);
    // Cocos2dx
    aList-> itemProvider = CC_CALLBACK_1 (AClass :: getListItemResource, this);
    // CocosCreator
    aList.itemProvider = this.getListItemResource.bind (this);

For lists with horizontal flow, vertical flow, and paging, unlike non-virtual lists, which have flow characteristics, the number of items in each row or column of a virtual list is fixed. When the list is initialized, a default item is created to measure this number.
If you still need to typeset the number of items per row or column and must use virtualization, then you can insert some empty components or empty graphics for placeholders and set their width according to actual needs to achieve that kind of Typographic effect.

Loop list

A circular list is a list that is connected end to end. The circular list must be a virtual list. To enable the circular list:

aList.SetVirtualAndLoop().

The loop list only supports single-row or single-column layouts. It does not support flow layouts and page layouts.
Because the loop list is connected end to end, specifying an item index may appear in different positions, so when you need to specify the rolling position, try to avoid using the item index. For example, if you need to scroll the list one space left / up or right / down, the best way is to call the ScrollPane API: ScrollLeft / ScrollRight / ScrollUp / ScrollDown
The characteristics of the circular list are the same as those of the virtual list, and will not be repeated here.