How to use the GroupListView component

The GroupListView class renders the items from a data collection similarly to a ListView, except that the data is hierarchical and items may be organized into groups. It includes support for selecting an item, scrolling, and custom layouts.

Live preview of the GroupListView component

The Basics

Start by creating a GroupListView control, and add it to the display list.

var groupListView = new GroupListView();
addChild(groupListView);

Data provider

To render some data in the group list view, pass in a collection that contains an object for each row.

var collection = new ArrayHierarchicalCollection( [
    {
        headerText: "Group A",
        children: [
            { text: "Node A1" },
            { text: "Node A2" },
            { text: "Node A3" },
            { text: "Node A4" }
        ]
    },
    {
        headerText: "Group B",
        children: [
            { text: "Node B1" },
            { text: "Node B2" },
            { text: "Node B3" }
        ]
    },
    {
        headerText: "Group C",
        children: [
            {text: "Node C1"}
        ]
    }
]);
groupListView.dataProvider = collection;

Set the collection's itemToChildren() method to get the children from each branch that need to be rendered by the tree view.

collection.itemToChildren = (item:Dynamic) -> item.children;

Set the itemToText() method to get the text from each item from the collection.

groupListView.itemToText = (item:Dynamic) -> item.text;

Additionally, set the itemToHeaderText() method to get the header text from each group in the collection.

groupListView.itemToHeaderText = (group:Dynamic) -> group.headerText;

Items in the collection are not required to be simple object literals, like {text: "Node A1"} in the example above. Instances of a class are allowed too (and encouraged as a best practice). If you use a class, be sure to update the item parameter's type in the itemToChildren, itemToText, and itemToHeaderText functions so that the compiler can catch any errors.

Selection

Add an event listener for Event.CHANGE to perform an action when the user selects a different item.

groupListView.addEventListener(Event.CHANGE, groupListView_changeHandler);

Check for the new value of the selectedItem property in the listener.

function groupListView_changeHandler(event:Event):Void {
    var groupListView = cast(event.currentTarget, GroupListView);
    trace("GroupListView selectedItem change: " + groupListView.selectedItem.data.text);
}

Alternatively, the value of the selectedLocation property references the location of the items in the group list view's collection as an Array of integers.

function groupListView_changeHandler(event:Event):Void {
    var groupListView = cast(event.currentTarget, GroupListView);
    trace("GroupListView selectedLocation change: " + groupListView.selectedLocation);
}

Add or remove items

To add a new item at a specific location, pass an object to the data provider's addAt() method.

var newItem = { text: "New Item" };
var newLocation = [2, 1];
groupListView.dataProvider.addAt(newItem, newLocation);

In the example above, a new tab is added to the beginning.

Similarly, to remove an item, call remove() or removeAt() on the collection.

var locationToRemove = [2, 1];
groupListView.dataProvider.removeAt(locationToRemove);

Item renderers

An item renderer is a Feathers UI component that displays a single item from a data collection inside a component like GroupListView and TreeView. In other words, a GroupListView typically contains multiple item renderers — with each one rendering a different item from the collection.

Feathers UI provides a default ItemRenderer class, which can display data in many different ways that cover a variety of common use-cases. However, components like GroupListView also support custom item renderers, which allow developers to render the list view's data in infinite unique ways.

Consider a collection of items with the following format.

{ name: "Pizza", icon: "https://example.com/img/pizza.png" }

While the default ItemRenderer class can easily display some text and an image, creating a custom item renderer for this simple data will be a good learning exercise.

A custom item renderer designed to display this data might use a Label to display the text, and an AssetLoader to display the image. The following example creates a DisplayObjectRecycler which instantiates these components and adds them to a LayoutGroupItemRenderer — a special base class for custom item renderers.

var recycler = DisplayObjectRecycler.withFunction(() -> {
    var itemRenderer = new LayoutGroupItemRenderer();

    var layout = new HorizontalLayout();
    layout.gap = 6.0;
    layout.paddingTop = 4.0;
    layout.paddingBottom = 4.0;
    layout.paddingLeft = 6.0;
    layout.paddingRight = 6.0;
    itemRenderer.layout = layout;

    var icon = new AssetLoader();
    icon.name = "loader";
    itemRenderer.addChild(icon);

    var label = new Label();
    label.name = "label";
    itemRenderer.addChild(label);

    return itemRenderer;
});

Developers are not required to use the LayoutGroupItemRenderer class. In fact, a custom item renderer may be created from any OpenFL display object, including primitives like openfl.display.Sprite and all other Feathers UI components.

Pass the DisplayObjectRecycler to the itemRendererRecycler property.

groupListView.itemRendererRecycler = recycler;

So far, the DisplayObjectRecycler creates the item renderer, but it doesn't understand how to interpret the data yet. A custom update() method on the recycler can do that.

recycler.update = (itemRenderer:LayoutGroupItemRenderer, state:GroupListViewItemState) -> {
    var label = cast(itemRenderer.getChildByName("label"), Label);
    var loader = cast(itemRenderer.getChildByName("loader"), AssetLoader);

    label.text = state.text;
    loader.source = state.data.icon;
};

When the update() method is called, it receives the item renderer and an GroupListViewItemState object. GroupListViewItemState has a number of useful properties.

In this case, the value of text is displayed by the Label, and the icon field from data (remember the example item from above, with name and icon fields) is displayed by the AssetLoader. Obviously, we'll need an itemToText() function to populate the text value from the name field.

groupListView.itemToText = function(item:Dynamic):String {
    return item.name;
};

It's always a good practice to provide a reset() method to the DisplayObjectRecycler, which will clean up a custom item renderer when it is no longer used by the GroupListView.

recycler.reset = (itemRenderer:LayoutGroupItemRenderer, state:GroupListViewItemState) -> {
    var label = cast(itemRenderer.getChildByName("label"), Label);
    var loader = cast(itemRenderer.getChildByName("loader"), AssetLoader);

    label.text = "";
    loader.source = null;
};

Warning: A DisplayObjectRecycler without a reset() method could potentially cause memory leaks or other unexpected behavior, if the same data needs to be used again later.

Styles

A number of styles may be customized on a GroupListView component, including an optional background skin and the appearance of the group list view's scroll bars.

Background skin

Optionally give the group list view a background using the backgroundSkin property. The following example sets it to a RectangleSkin instance.

var skin = new RectangleSkin();
skin.border = SolidColor(1.0, 0x999999);
skin.fill = SolidColor(0xcccccc);
skin.width = 16.0;
skin.height = 16.0;
groupListView.backgroundSkin = skin;

The border and fill properties of the RectangleSkin are used to adjust its appearance. They support a variety of values — from solid colors to gradients to bitmaps.

The group list view automatically calculates its preferred size based on the initial dimensions of its background skin (accounting for some other factors too, like the layout and scroll bars), so it's important to set a skin's width and height properties to appropriate values to use in this calculation.

See Skinning with common shapes for more details about how to use RectangleSkin with the LineStyle and FillStyle enums that change its border and fill appearance.

The appearance of the group list view's border or fill may be customized to change when the group list view is disabled. In the next example, setting the skin's disabledFill method makes it switch to a different fill when the group list view is disabled.

skin.disabledFill = SolidColor(0xffcccc);

Similarly, use the skin's disabledBorder property to change the border when disabled.

skin.disabledBorder = SolidColor(2.0, 0x999999);

In the examples above, the group list view uses the same RectangleSkin for all states, and that skin listens for changes to the group list view's current state. Alternatively, the group list view's disabledBackgroundSkin method allows the group list view to display a completely different display object when it is disabled.

var defaultSkin = new RectangleSkin();
// ... set border, fill, width, and height
groupListView.backgroundSkin = defaultSkin;

var disabledSkin = new RectangleSkin();
// ... set border, fill, width, and height
groupListView.disabledBackgroundSkin = disabledSkin;

In the example above, the group list view will have a separate skins when enabled and disabled.

Layout

Set the group list view's layout property to change how its children are positioned and sized. By default, a list view uses VerticalListLayout, but it may be changed to a different layout, if desired.

groupListView.layout = new HorizontalListLayout();

The example above uses HorizontalListLayout, but a number of different layouts are available in Feathers UI, and it's also possible to create custom layouts.

Scroll bars

The scroll bars in a GroupListView component are of type HScrollBar and VScrollBar. Their appearance may be customized globally in a theme, or they may be customized outside of a theme on an specific, individual group list view.

See How to use the HScrollBar and VScrollBar components for complete details about which styles are available for the scroll bars.

Style scroll bars globally

Use the HScrollBar and VScrollBar classes in a theme to provide a function that globally styles all scroll bars in your project.

styleProvider.setStyleFunction(HScrollBar, null, setHScrollBarStyles);
styleProvider.setStyleFunction(VScrollBar, null, setVScrollBarStyles);

The functions should use the following signatures.

function setHScrollBarStyles(scrollBar:HScrollBar):Void {
    // ... set styles here
}

function setVScrollBarStyles(scrollBar:VScrollBar):Void {
    // ... set styles here
}

Style scroll bars in a specific GroupListView

The scrollBarXFactory and scrollBarYFactory properties may be used to customize the creation of an individual group list view's scroll bars.

groupListView.scrollBarXFactory = () -> {
    var scrollBar = new HScrollBar();
    // ... set styles here
    return scrollBar;
};

groupListView.scrollBarYFactory = () -> {
    var scrollBar = new VScrollBar();
    // ... set styles here
    return scrollBar;
};