Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX - changing order of children in a FlowPane

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?

like image 249
taranion Avatar asked Sep 06 '13 22:09

taranion


People also ask

What is the difference between a Flowpane and a VBox or HBox why would you use one over the other?

FlowPane – lays out its children in a flow that wraps at the flowpane's boundary. HBox – arranges its content nodes horizontally in a single row. VBox – arranges its content nodes vertically in a single column.

How the nodes in a grid pane are arranged?

If we use Grid Pane in our application, all the nodes that are added to it are arranged in a way that they form a grid of rows and columns.

What is the difference between HBox and VBox pane?

HBox is a subclass of Pane that lays out its children next to each other, in a horizontal row. VBox is a subclass of Pane that lays out its children in vertical column. An HBox might be used as the bottom component in a BorderPane, making it possible to line up several components along the bottom of the border pane.

Can you add multiple panes to a scene in JavaFX?

The Scene it self can only have one root Pane. So if you want 2 panes in the Scene you need 3.


1 Answers

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);
        }
    }
}
like image 106
jewelsea Avatar answered Oct 05 '22 00:10

jewelsea