JavaFX - changing order of children in a FlowPane


Question

I have got a JavaFX 2.2 FlowPane and want it's children to be change their order on demand.

I found out that

Collections.sort( getChildren(), new Comparator(){...} );

causes the exception

java.lang.IllegalArgumentException: Children: duplicate children added: parent = ...

I also tried to sort using the child Nodes toFront() method (as suggested here How to change order of children in JavaFX), but this does not seem to influence the list order.

Is there anything else I could try or are children in a scene graph unmutable regarding their order?

1
4
5/23/2017 12:00:43 PM

Update: Errata

Due to a bug in JavaFX 2.x, the sample solution for this answer will not work in JavaFX 2.x. Thanks to assylias for identifying the JavaFX 2.x bug:

When I run your program, the list does not update until I resize the window horizontally (vertically does not do anything) - jdk 7u25 / Windows 7 x64.

The bug is fixed in Java 8, so if you use Java 8, the sample solution will work.

Solution Strategy

  1. Create a new collection based on the flow pane children.
  2. Sort the new collection.
  3. Set the children of the flow pane to the contents of the sorted collection.

Code Snippet

ObservableList<Node> workingCollection = FXCollections.observableArrayList(
    flow.getChildren()
);

Collections.sort(workingCollection, new NodeComparator());

flow.getChildren().setAll(workingCollection);

Sample Output

Sample output before sorting:

unsorted

Sample output after sorting:

sorted

Sample Application

import javafx.application.Application;
import javafx.collections.*;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

import java.util.*;

public class FlowGo extends Application {
    private final ObservableList<Label> labels = FXCollections.observableArrayList(
        createLabel("oranges"),
        createLabel("apples"),
        createLabel("pears"),
        createLabel("peaches"),
        createLabel("bananas")
    );

    @Override public void start(Stage stage) {
        FlowPane flow = createFlow();

        VBox layout = new VBox(10);
        layout.getChildren().setAll(
            flow,
            createActionButtons(flow)
        );
        layout.setAlignment(Pos.CENTER);
        layout.setStyle("-fx-padding: 10px; -fx-background-color: cornsilk;");

        stage.setScene(new Scene(layout));
        stage.show();
    }

    private HBox createActionButtons(final FlowPane flow) {
        Button sort = new Button("Sort");
        sort.setOnAction(new EventHandler<ActionEvent>() {
            @Override public void handle(ActionEvent actionEvent) {
                ObservableList<Node> workingCollection = FXCollections.observableArrayList(
                    flow.getChildren()
                );

                Collections.sort(workingCollection, new NodeComparator());

                flow.getChildren().setAll(workingCollection);
            }
        });

        Button reset = new Button("Reset");
        reset.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                flow.getChildren().setAll(labels);
            }
        });

        HBox buttonBar = new HBox(10);
        buttonBar.getChildren().addAll(
            sort,
            reset
        );
        buttonBar.setAlignment(Pos.CENTER);

        return buttonBar;
    }

    private FlowPane createFlow() {
        FlowPane flow = new FlowPane();
        flow.setHgap(10);
        flow.setVgap(10);
        flow.getChildren().setAll(labels);
        flow.setAlignment(Pos.CENTER);
        flow.setStyle("-fx-padding: 10px; -fx-background-color: lightblue; -fx-font-size: 16px;");

        return flow;
    }

    private Label createLabel(String text) {
        Label label = new Label(text);
        label.setUserData(text);

        return label;
    }

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

    private static class NodeComparator implements Comparator<Node> {
        @Override
        public int compare(Node o1, Node o2) {
            String s1 = (String) o1.getUserData();
            String s2 = (String) o2.getUserData();

            return s1.compareTo(s2);
        }
    }
}
6
9/7/2013 1:51:33 AM

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon