Question

Is there a way to only "render" an component in Javafx if a condition is met? I am interested in doing a user interface with different roles and just add a component if the role allows it, also I would like to keep working with FXML. I haven't read about anything like that.

Was it helpful?

Solution

Bind visibility

Bind the visible property of your component to a BooleanExpression representing the condition under which it should be visible.

FXML can do binding, but note that as of JavaFX 2.2 "Only simple expressions that resolve to property values or page variables are currently supported. Support for more complex expressions involving boolean or other operators may be added in the future."

If you don't bind in FXML, you can bind in Java code using the JavaFX API.

node.visibleProperty().bind(conditionProperty);

If you don't want the invisible property to take up layout space, also first bind the managed property to the visible property.

node.managedProperty().bind(node.visibleProperty());

Alternately change visibility in change listeners

Note that setting up a binding for a full role based system is pretty complex, so for such a task you are better off using change listeners and coding part of your logic in Java rather than trying to do everything in FXML. You will still be able to design your UI in FXML, but you can add some code in your controller to manage what items are visible for what role.

Sample role based display

Here is a role based solution using the Java API.

The solution shows different labeled pictures depending on the selected roles.

roles

import javafx.application.Application;
import javafx.beans.value.*;
import javafx.collections.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

import java.util.*;

enum Role { father, son, mother, daughter, brother, sister }

class RoleManager {
    private final Map<Node, List<Role>> nodeRoles = new HashMap<>();
    private ObservableList<Role> activeRoles;

    public final ListChangeListener<Role> ACTIVE_ROLE_LISTENER = new ListChangeListener<Role>() {
        @Override
        public void onChanged(Change<? extends Role> c) {
            showActiveNodes();
        }
    };

    public void setActiveRoles(ObservableList<Role> activeRoles) {
        if (this.activeRoles != null) {
            this.activeRoles.removeListener(ACTIVE_ROLE_LISTENER);
        }
        this.activeRoles = activeRoles;
        this.activeRoles.addListener(ACTIVE_ROLE_LISTENER);
    }

    public void showActiveNodes() {
        for (Node node : nodeRoles.keySet()) {
            node.setVisible(isActive(node));
        }
    }

    public void assignRole(Node node, Role... roles) {
        nodeRoles.put(node, Arrays.asList(roles));
    }

    private boolean isActive(Node node) {
        if (activeRoles == null) {
            return false;
        }

        for (Role role: nodeRoles.get(node)) {
            if (activeRoles.contains(role)) {
                return true;
            }
        }

        return false;
    }
}

public class RoleVisibility extends Application {

    private RoleManager roleManager = new RoleManager();

    @Override
    public void start(Stage stage) {
        VBox layout = new VBox(10);
        layout.getChildren().setAll(
            getRoleChooser(),
            createContent()
        );
        layout.setStyle("-fx-padding: 10px; -fx-background-color: cornsilk;");

        roleManager.showActiveNodes();

        stage.setTitle("Role Selector");
        stage.setScene(new Scene(layout));
        stage.show();
    }

    private Node getRoleChooser() {
        ObservableList<Role> activeRoles = FXCollections.observableArrayList();

        VBox roleChooser = new VBox(10);
        for (final Role role: Role.values()) {
            CheckBox checkBox = new CheckBox(role.toString());
            checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> observable, Boolean wasSelected, Boolean isSelected) {
                    if (isSelected) {
                        activeRoles.add(role);
                    } else {
                        activeRoles.remove(role);
                    }
                }
            });

            roleChooser.getChildren().add(checkBox);
        }

        roleManager.setActiveRoles(
            activeRoles
        );

        return roleChooser;
    }

    private Pane createContent() {
        HBox content = new HBox(10);

        // icon license:
        //License: Free for non-commercial use.
        //Commercial usage: Not allowed
        //The products or characters depicted in these icons are © by Lucasfilm Ltd.

        content.getChildren().addAll(
            createLabel("Darth Vader",    "Vader-03-icon.png",          Role.father),
            createLabel("Queen Amidala",  "Padme-Amidala-icon.png",     Role.mother),
            createLabel("Luke Skywalker", "Luke-Skywalker-01-icon.png", Role.brother,  Role.son),
            createLabel("Princess Leia",  "Leia-icon.png",              Role.daughter, Role.sister)
        );

        return content;
    }

    private Label createLabel(String text, String graphic, Role... roles) {
        Label label = new Label(
            text,
            new ImageView(
                new Image(
                    "http://icons.iconarchive.com/icons/jonathan-rey/star-wars-characters/128/" + graphic
                )
            )
        );

        label.setContentDisplay(ContentDisplay.TOP);

        roleManager.assignRole(label, roles);

        return label;
    }


    public static void main(String[] args) {
        launch(args);
    }
}

FXML Based Solution

I was interested in what it would take to get something like this to work with FXML, so I created a small framework to handle role based FXML UIs. It could perhaps have some performance enhancements and add some convenience definitions for defining role based controllers as well as shorthands for role definitions in the FXML, but it does seem to work in principle and demonstrate a basic approach.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top