How to use the TreeView component
The TreeView
class renders the items from a data collection similarly to a ListView
, except that the data is hierarchical, and branches may be opened and closed to reveal nested data. It includes support for selecting an item, scrolling, and custom layouts.
The Basics
Start by creating a TreeView
control, and add it to the display list.
var treeView = new TreeView();
addChild(treeView);
Data provider
To render some data in the tree view, pass in a hierarchical collection that contains an object for each row.
var collection = new ArrayHierarchicalCollection([
{
text: "Node 1",
children: [
{
text: "Node 1A",
children: [
{ text: "Node 1A-I" },
{ text: "Node 1A-II" }
]
},
{ text: "Node 1B" },
]
},
{ text: "Node 2" },
{
text: "Node 3",
children: [
{ text: "Node 3A" },
{ text: "Node 3B" },
{ text: "Node 3C" }
]
}
]);
treeView.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 to display from the collection.
treeView.itemToText = (item:Dynamic) -> item.text;
Items in the collection are not required to be simple object literals, like
{text: "Node 1"}
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 theitemToChildren
anditemToText
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.
treeView.addEventListener(Event.CHANGE, treeView_changeHandler);
Check for the new value of the selectedItem
property in the listener.
function treeView_changeHandler(event:Event):Void {
var treeView = cast(event.currentTarget, TreeView);
trace("TreeView selectedItem change: " + treeView.selectedItem.data.text);
}
Alternatively, the value of the selectedLocation
property references the location of the items in the tree view's collection as an Array
of integers.
function treeView_changeHandler(event:Event):Void {
var treeView = cast(event.currentTarget, TreeView);
trace("TreeView selectedLocation change: " + treeView.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];
treeView.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];
treeView.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 TreeView
and ListView
. In other words, a TreeView
typically contains multiple item renderers — with each one rendering a different item from the collection.
Feathers UI provides a default HierarchicalItemRenderer
class, which can display data in many different ways that cover a variety of common use-cases. However, components like TreeView
also support custom item renderers, which allow developers to render the tree 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 HierarchicalItemRenderer
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 openedToggle = new ToggleButton();
openedToggle.name = "openedToggle";
itemRenderer.addChild(openedToggle);
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 likeopenfl.display.Sprite
and all other Feathers UI components.
Pass the DisplayObjectRecycler
to the itemRendererRecycler
property.
treeView.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:TreeViewItemState) -> {
var openedToggle = cast(itemRenderer.getChildByName("openedToggle"), ToggleButton);
var label = cast(itemRenderer.getChildByName("label"), Label);
var loader = cast(itemRenderer.getChildByName("loader"), AssetLoader);
openedToggle.visible = state.branch;
openedToggle.selected = state.branch && state.opened;
label.text = state.text;
loader.source = state.data.icon;
};
When the update()
method is called, it receives the item renderer and an TreeViewItemState
object. TreeViewItemState
has a number of useful properties.
data
is the item from the collection.branch
indicates if the item is a branch or not.enabled
indicates if the item renderer should be enabled or not.layoutIndex
is the position of the item within the layout.location
is the position of the item within the collection.opened
indicates if a branch is opened or not.owner
is theTreeView
that contains the item.selected
is populated by comparing toselectedItem
.text
is populated usingitemToText()
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
. The values of branch
and opened
are used with a ToggleButton
to display whether a branch is opened or not. Obviously, we'll need an itemToText()
function to populate the text
value from the name
field.
treeView.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 TreeView
.
recycler.reset = (itemRenderer:LayoutGroupItemRenderer, state:TreeViewItemState) -> {
var label = cast(itemRenderer.getChildByName("label"), Label);
var loader = cast(itemRenderer.getChildByName("loader"), AssetLoader);
label.text = "";
loader.source = null;
};
Warning: A
DisplayObjectRecycler
without areset()
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 TreeView
component, including an optional background skin and the appearance of the tree view's scroll bars.
Background skin
Optionally give the tree 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;
treeView.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 tree 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 theLineStyle
andFillStyle
enums that change its border and fill appearance.
The appearance of the tree view's border or fill may be customized to change when the tree view is disabled. In the next example, setting the skin's disabledFill
method makes it switch to a different fill when the tree 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 tree view uses the same RectangleSkin
for all states, and that skin listens for changes to the tree view's current state. Alternatively, the tree view's disabledBackgroundSkin
method allows the tree view to display a completely different display object when it is disabled.
var defaultSkin = new RectangleSkin();
// ... set border, fill, width, and height
treeView.backgroundSkin = defaultSkin;
var disabledSkin = new RectangleSkin();
// ... set border, fill, width, and height
treeView.disabledBackgroundSkin = disabledSkin;
In the example above, the tree view will have a separate skins when enabled and disabled.
Layout
Set the tree 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.
treeView.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 TreeView
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 tree view.
See How to use the
HScrollBar
andVScrollBar
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 TreeView
The scrollBarXFactory
and scrollBarYFactory
properties may be used to customize the creation of an individual tree view's scroll bars.
treeView.scrollBarXFactory = () -> {
var scrollBar = new HScrollBar();
// ... set styles here
return scrollBar;
};
treeView.scrollBarYFactory = () -> {
var scrollBar = new VScrollBar();
// ... set styles here
return scrollBar;
};