javajavafxchildrenhboxvbox

HBox displaying Nodes incorrectly, even though getChildren() displays them in the correct order


Ok, here it goes, I have tinkered all day without getting to a solution:

I have an HBox displayed on-screen, which contains multiple StackPanel elements, each containing one Circle and one Label element, so that both are displayed on top of themselves. The StackPanels are the Nodes of the HBox, and were added based on an Array, not through a xfml file. The HBox is inside a VBox, which contains 3 other elements that are irrelevant here.

My objective is to switch two of those nodes using an animation, which I created with a PathTransition that has a Path of an Arc. I have two such animations running at the same time, one for the Node going to the destination, and one for the other one coming to the current location.

After the animation is completed, I use the switchNode() function to switch the two nodes, as I want their order to change, as I want to continue performing animations, and the animation itself does not change their order, only location. Per example, HBox.getChildren().etc(casting it into text) would give me 0,1,2,3,4 before and 3,1,2,0,4 after the switch if i switched the 0th and the 3rd index.

The swap appers to be successfull, but problem is that they are not displayed in the swapped locations.

Before Swap Swapping After Swamp

public class AnimationController2 {
private int[] arr = new int[] { 0, 1, 2, 3, 4, 5 };

@FXML
private VBox vbox;
@FXML
private HBox content;

@FXML
private HBox indexes;

@FXML
private void switchToInput() throws IOException {
    App.setRoot("input");
}

@FXML
private void repeat() {
    animate(0, 3); //should work for any index and be repeatable
}

public void initialize() {
    addElements();
}

public void animate(int from, int to) {
    PathTransition move1 = new PathTransition();
    PathTransition move2 = new PathTransition();
    Node node1 = content.getChildren().get(from);
    Node node2 = content.getChildren().get(to);
    move1.setNode(node1);
    move2.setNode(node2);
    move1.setDuration(Duration.seconds(3));
    move2.setDuration(Duration.seconds(3));
    move1.setPath(makePath(node1, node2, true));
    move2.setPath(makePath(node2, node1, false));
    move2.setOnFinished(b -> {
        switchNode(from, to);
    });
    move1.play();
    move2.play();
}

public void switchNode(int a, int b) {
    ObservableList<Node> workingCollection = FXCollections.observableArrayList(content.getChildren());
    /*
     * Debugging
     * System.out.println(" ");
     * System.out.println("Original: ");
     * for (Node i : workingCollection) {
     * Label aa = (Label) ((StackPane) i).getChildren().get(1);
     * System.out.print(aa.getText() + " ");
     * }
     */

    Collections.swap(workingCollection, a, b);

    /*
     * Debugging
     * System.out.println(" ");
     * System.out.println("Switched: ");
     * for (Node i : workingCollection) {
     * Label aa = (Label) ((StackPane) i).getChildren().get(1);
     * System.out.print(aa.getText() + " ");
     * }
     */
    content.getChildren().setAll(workingCollection);
}

public Path makePath(Node from, Node to, boolean right) {
    // creating variables
    double distance = to.getLayoutX() - from.getLayoutX();
    double x = from.boundsInLocalProperty().get().getCenterX();
    double y = from.boundsInLocalProperty().get().getCenterY();
    double fromX = x;
    double fromY = y;
    double toX = x + distance; // 300
    double toY = y;
    ArcTo arcTo = new ArcTo();
    arcTo.setLargeArcFlag(false);
    arcTo.setSweepFlag(true);
    arcTo.setRadiusX((toX + fromX) / 2);
    arcTo.setRadiusY(right ? 200 : distance / 3); // optimal values
    arcTo.setX(toX);
    arcTo.setY(toY);
    Path path = new Path();
    path.getElements().addAll(new MoveTo(fromX, fromY), arcTo);
    return path;
}
public void addElements() {
    for (int i = 0; i < arr.length; i++) {
        Circle circle = new Circle(30, 30, 30);
        circle.setFill(Color.BLUE);
        Label label = new Label(arr[i] + "");
        label.setTextFill(Color.BLACK);
        StackPane join = new StackPane();
        join.getChildren().addAll(circle, label);
        content.getChildren().add(join);
        Label index = new Label(i + "");
        indexes.getChildren().add(index);
        indexes.setSpacing(62);
    }
}

}

public class App extends Application {
private static Scene scene;

@Override
public void start(Stage stage) throws IOException {
    scene = new Scene(loadFXML("animation"), 640, 480);
    stage.setScene(scene);
    stage.setTitle("Array2Tree");
    stage.show();
}

static void setRoot(String fxml) throws IOException {
    scene.setRoot(loadFXML(fxml));
}

private static Parent loadFXML(String fxml) throws IOException {
    FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
    return fxmlLoader.load();
}

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

}

FXML

<VBox fx:id = "vbox" alignment="CENTER" spacing="20.0" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.example.AnimationController2">
<children>
    <HBox alignment="CENTER" spacing="10.0" fx:id = "content" ></HBox>
    <HBox alignment="CENTER" spacing="10.0" fx:id = "indexes" ></HBox>
    <Label text="Animation View" />
    <Button fx:id="InputButton" onAction="#switchToInput" text="Switch to Input" />
    <Button fx:id = "Repeat" onAction="#repeat" text="Repeat" />
</children>
<padding>
    <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>

Solution

  • Thank you to @jewelsea for commenting.

    My problem was solved by adding

    workingCollection.get(a).setTranslateX(0);
    workingCollection.get(b).setTranslateX(0);
    

    before

    Collections.swap(workingCollection, a, b);