Is there an easy way to check for wrapped elements inside a FlowPane or determine the index at which the elements are wrapped?
Asked
Active
Viewed 1,181 times
1 Answers
2
I don't know if it qualifies as "easy", but you can check the boundsInParent
for each child and compare it to the first child. For a horizontal FlowPane
this should work:
private List<Node> findWrapped(FlowPane flow) {
List<Node> wrapped = new ArrayList<>();
if (flow.getChildren().size() == 0) {
return wrapped ;
}
double y = flow.getChildren().get(0).getBoundsInParent().getMaxY();
for (Node child : flow.getChildren()) {
if (child.getBoundsInParent().getMinY() >= y) {
wrapped.add(child);
}
}
return wrapped ;
}
SSCCE:
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class FlowPaneWrappedNodes extends Application {
@Override
public void start(Stage primaryStage) {
FlowPane flow = new FlowPane();
for (int i = 1 ; i <= 15; i++) {
flow.getChildren().add(createPane(i));
}
Button button = new Button("Find wrapped");
button.setOnAction(e ->
findWrapped(flow).stream().map(Node::getId).forEach(System.out::println));
BorderPane root = new BorderPane(flow, null, null, button, null);
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private List<Node> findWrapped(FlowPane flow) {
List<Node> wrapped = new ArrayList<>();
if (flow.getChildren().size() == 0) {
return wrapped ;
}
double y = flow.getChildren().get(0).getBoundsInParent().getMaxY();
for (Node child : flow.getChildren()) {
if (child.getBoundsInParent().getMinY() >= y) {
wrapped.add(child);
}
}
return wrapped ;
}
private Pane createPane(int id) {
Pane pane = new StackPane();
pane.setMinSize(50, 50);
pane.setId("Pane "+id);
Label label = new Label(Integer.toString(id));
pane.getChildren().add(label);
return pane ;
}
public static void main(String[] args) {
launch(args);
}
}
For a vertical flow pane, you can do something similar, comparing the x
values of the bounds instead of the y
.
Update:
Making this recompute automatically is a bit tricky. You basically need to observe the child list of the FlowPane
(in case new nodes are added or removed), and then observe the bounds of each node in the flow pane. You can do this with code like this:
FlowPane flow = new FlowPane();
ListView<Node> wrappedNodeView = new ListView<>();
ChangeListener<Bounds> boundsListener = (obs, oldBounds, newBounds) ->
wrappedNodeView.getItems().setAll(findWrapped(flow));
flow.getChildren().addListener((Change<? extends Node> c) -> {
while (c.next()) {
if (c.wasAdded()) {
c.getAddedSubList().forEach(node -> node.boundsInParentProperty().addListener(boundsListener));
}
if (c.wasRemoved()) {
c.getRemoved().forEach(node -> node.boundsInParentProperty().removeListener(boundsListener));
}
}
wrappedNodeView.getItems().setAll(findWrapped(flow));
});
(This creates a list view which displays all the "wrapped" nodes. You can obviously just update a list or whatever as you need.)
Here it is built into the previous SSCCE:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class FlowPaneWrappedNodes extends Application {
@Override
public void start(Stage primaryStage) {
FlowPane flow = new FlowPane();
ListView<Node> wrappedNodeView = new ListView<>();
ChangeListener<Bounds> boundsListener = (obs, oldBounds, newBounds) ->
wrappedNodeView.getItems().setAll(findWrapped(flow));
flow.getChildren().addListener((Change<? extends Node> c) -> {
while (c.next()) {
if (c.wasAdded()) {
c.getAddedSubList().forEach(node -> node.boundsInParentProperty().addListener(boundsListener));
}
if (c.wasRemoved()) {
c.getRemoved().forEach(node -> node.boundsInParentProperty().removeListener(boundsListener));
}
}
wrappedNodeView.getItems().setAll(findWrapped(flow));
});
wrappedNodeView.setCellFactory(lv -> new ListCell<Node>() {
@Override
public void updateItem(Node item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText("");
} else {
setText(item.getId());
}
}
});
Button button = new Button("Add pane");
button.setOnAction(e ->
flow.getChildren().add(createPane(flow.getChildren().size()+1)));
BorderPane root = new BorderPane(flow, null, wrappedNodeView, button, null);
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private ObservableList<Node> findWrapped(FlowPane flow) {
ObservableList<Node> wrapped = FXCollections.observableArrayList();
if (flow.getChildren().size() == 0) {
return wrapped ;
}
double y = flow.getChildren().get(0).getBoundsInParent().getMaxY();
for (Node child : flow.getChildren()) {
if (child.getBoundsInParent().getMinY() >= y) {
wrapped.add(child);
}
}
return wrapped ;
}
private Pane createPane(int id) {
Pane pane = new StackPane();
pane.setMinSize(50, 50);
pane.setId("Pane "+id);
Label label = new Label(Integer.toString(id));
pane.getChildren().add(label);
return pane ;
}
public static void main(String[] args) {
launch(args);
}
}

James_D
- 201,275
- 16
- 291
- 322
-
How would I apply it automatically? I'd want it to update it on resize of the FlowPane, but a widthProperty-Listener isn't working quite that good. Another possiblity would be to tell the FlowPane to grow with a AnchorPoint at the top and not in the center and then set VGap on something like 500. – Dmitrij D Oct 31 '15 at 23:06
-
why go that long way, from off the top i would say iterate over the children, get the one with the largest side(width) and its the wrapped size, since its the largest. idk, just guessing out here. – Elltz Nov 01 '15 at 00:17
-
@DmitrijD That's... a bit tricky, to get it to work under any possible changes. Have a look at the updated answer. – James_D Nov 01 '15 at 01:37
-
@Elltz Sure, there may be easier ways, though I admit I don't really understand what you are outlining there. (Why would the largest width of a node be the "wrapping width"?) Feel free to post an alternative answer if you have a simpler way of doing it. – James_D Nov 01 '15 at 01:38