Skip to content

Tree and List

raeleus edited this page Jan 12, 2021 · 29 revisions

Using Trees and Lists is a great way to organize selectable data. List is usually used as an element of a SelectBox, however it can still be used on its own. Trees can organize data like an expandable file explorer.

ListStyle

The "background" drawable is the box that contains the entire contents of the widget. Do not add too much padding here or your selections will look a little odd.
list

Lists require a font to display their items. The user can click on any of the items to select it. To distinguish between selected and unselected items, set the "fontColorSelected" and "fontColorUnselected" color fields.
font

Another way to make selected items stand out is to specify a "selection" drawable. It could technically be a single pixel sized image that will be stretched, however you'll want to add some padding to your selection drawable if you find your text getting squished against the background. You can also add "over" and "down" drawables for when the user mouses over and clicks on these items.
selection
padding-example

List Layout

If you just need a simple list with a few text options, it's easy enough to use #setItems() to do so.

List<String> list = new List(skin);
list.setItems(new String[] {"selection one", "selection two", "selection three"});
root.add(list);

image

List aligns to the left by default. There are times I like to set the alignment to the center especially if it's part of a SelectBox that is centered.

list.setAlignment(Align.center);

image

Little known fact: you can set a list to automatically select an entry as the user types the words. This requires the list to be clicked on or have keyboard focus assigned to it programmatically. Notice that you can completely type "aab" and selection will shift from "aaa" to "aab" appropriately.

List<String> list = new List(skin);
list.setItems(new String[] {"aaa", "aab", "bbb", "ccc", "ddd"});
list.setTypeToSelect(true);
root.add(list);
stage.setKeyboardFocus(list);

List accepts any kind of object as the array of items, but only implements them as text via Object#toString(). You can't add drawables or widgets to a list and expect them to display inside the list.

List list = new List(skin);
list.setItems(new Object[] {"string", new Label("label", skin), skin.getDrawable("button-normal")});
root.add(list);

image

Having a List of a specific type can be very useful when you need to use the selected Object to do a task.

final List<Drawable> list = new List(skin);
list.setItems(new Drawable[] {skin.getDrawable("checkbox"), skin.getDrawable("radio"), skin.getDrawable("button-normal")});
root.add(list);

root.row();
final Image image = new Image();
root.add(image).space(10);

list.addListener(new ChangeListener() {
    @Override
    public void changed(ChangeEvent event, Actor actor) {
        image.setDrawable(list.getSelected());
    }
});

suAJByPHPn

TreeStyle

The main purpose of the Tree widget is to expand and collapse data. To properly convey this, you need drawables as icons for the "plus" and "minus" fields. If you're trying to convey a file directory, for example, you'll use folder graphics to represent this. If it's a drop down bulleted list, simple arrows or plus/minus icons will be enough.
tree-plus tree-minus
tree-minus_00 tree-minus-over_00
plus minus

You can also specify mouse-over variations with plusOver and minusOver.
tree-minus_13 tree-plus_13

There are also fields for "background" and "over" if you want to add some style to each line. I don't find this incredibly useful unless you want the tree items to be selectable. In that case, make sure to fill something in for the "selection" field.
list list-selection

Tree Layout

Implementing a Tree widget in your UI isn't exactly a straightforward process. You need to make a subclass of Tree.Node. Users must click the plus icon to expand that node's values.

@Override
public void create () {
    ...
    Tree tree = new Tree(skin);

    Label label = new Label("Folder", skin);
    BasicNode node = new BasicNode(label);

    label = new Label("child1", skin);
    BasicNode node1 = new BasicNode(label);
    node.add(node1);

    label = new Label("child2", skin);
    node1 = new BasicNode(label);
    node.add(node1);
    tree.add(node);

    label = new Label("Folder2", skin);
    node = new BasicNode(label);

    label = new Label("child1", skin);
    node1 = new BasicNode(label);
    node.add(node1);

    label = new Label("child2", skin);
    node1 = new BasicNode(label);
    node.add(node1);
    tree.add(node);
    root.add(tree);
    stage.addActor(root);
}

public static class BasicNode extends Tree.Node {
    public BasicNode(Actor actor) {
        super(actor);
    }
}

image

Why is Node so complicated? It is designed this way so that the generic type parameters don't need to be specified repeatedly. This can simplify your code tremendously.

@Override
public void create () {
    ...
    Tree tree = new Tree(skin);
    
    TextNode node = new TextNode("Folder");
    
    TextNode node1 = new TextNode("child1");
    node.add(node1);
    
    node1 = new TextNode("child2");
    node.add(node1);
    tree.add(node);
    
    node = new TextNode("Folder2");
    
    node1 = new TextNode("child1");
    node.add(node1);
    
    node1 = new TextNode("child2");
    node.add(node1);
    tree.add(node);
    root.add(tree);
    stage.addActor(root);
}

public class TextNode extends Tree.Node<TextNode, String, Label> {
    public TextNode(String text) {
        super(new Label(text, skin));
        setValue(text);
    }
}

You can set the icon of a particular node.

node1.setIcon(skin.getDrawable("icon"));

image

Tree has various options on spacing.

tree.setIconSpacing(0, 10);
tree.setIndentSpacing(50);
tree.setYSpacing(10);

image

If you want your tree to have all nodes visible, you'll want to use Tree#expandAll(). Remember to use this after you define all your nodes or it will not work.

tree.expandAll();

Further steps

Widgets often extend past the limited bounds of their parent and you need a way to contain them. Proceed with the next chapter, 06 - ScrollPane or return to the table of contents.

Clone this wiki locally