I'm using a GridPane
to stock informations about cities (in a game), but sometimes I want to delete some lines. This is the Class I use for my GridPane
:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.franckyi.kingsim.city.City;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Text;
public class CityGrid extends GridPane {
private List<City> cities;
public CityGrid() {
super();
cities = new ArrayList<City>();
}
public List<City> getCities() {
return cities;
}
public void deleteCity(int row) {
cities.remove(row - 1);
removeNodes(getNodesFromRow(row));
int i = row;
while (!getNodesFromRow(i + 1).isEmpty()) {
moveNodes(i + 1, getNodesFromRow(i + 1));
removeNodes(getNodesFromRow(i + 1));
i++;
}
}
public void addCity(City city) {
cities.add(city);
int col = 0;
List<Node> rowNodes = new ArrayList<Node>();
Button mod = new Button("Modify");
mod.setOnAction(event -> {
});
Button del = new Button("Delete");
del.setOnAction(event -> {
deleteCity(getRowIndex(del));
});
rowNodes.addAll(Arrays.asList(new CheckBox(), new Text(city.getName()), new Text(city.getStatus().getText()),
new Text(city.getTrade() + ""), new Text(city.getCost() + ""), new Text(city.getTroops() + ""), mod,
del));
for (Node n : rowNodes) {
n.setId(cities.size() + "");
this.add(n, col, cities.size());
col++;
}
}
private List<Node> getNodesFromRow(int i) {
List<Node> list = new ArrayList<Node>();
for (Node n : getChildren()) {
if (getRowIndex(n).equals(i)) {
list.add(n);
}
}
System.out.println(list.size());
return list;
}
private void removeNodes(List<Node> list) {
for (Node node : list) {
this.getChildren().remove(getIndex(getColumnIndex(node), getRowIndex(node)));
}
}
private void moveNodes(int row, List<Node> nodes) {
int i = 0;
for (Node node : getNodesFromRow(row)) {
this.getChildren().set(getIndex(getColumnIndex(node), getRowIndex(node)),
nodes.get(i));
i++;
}
}
private int getIndex(int col, int row) {
int i = 0;
for (Node node : getChildren()) {
if (getColumnIndex(node) == col && getRowIndex(node) == row)
return i;
i++;
}
return 0;
}
}
The addCity
function works perfectly. The deleteCity
function also works when I delete the last city added. But when I delete a city, it automatically deletes ALL the cities added after the one I delete, and I don't want that.
You should also notice that everytime the getNodesFromRow(int i)
method is executed, it prints the number of Nodes in the selected row. When I add two cities and I delete the first one, this is what I get in the console : 8, 0, 8, 8, 8, 8, 8, 0.
Can someone help me ? (Tell me if you want all the code needed to reproduce it at home)
private void moveNodes(int row, List<Node> nodes) {
int i = 0;
for (Node node : getNodesFromRow(row)) {
this.getChildren().set(getIndex(getColumnIndex(node), getRowIndex(node)),
nodes.get(i));
i++;
}
}
This does not work. The index in the child list has no meaning in a GridPane
other then the order in which they are drawn. The row/column index is saved to the properties
map of each child. To modify these, you need to use the static GridPane.setRowIndex
method.
Example
@Override
public void start(Stage primaryStage) {
Button btn = new Button("Delete");
TextField tf = new TextField();
TextFormatter<Integer> formatter = new TextFormatter<>(new IntegerStringConverter());
formatter.setValue(0);
tf.setTextFormatter(formatter);
btn.disableProperty().bind(IntegerExpression.integerExpression(formatter.valueProperty()).lessThan(0));
GridPane grid = new GridPane();
grid.setHgap(5);
grid.setVgap(5);
btn.setOnAction((ActionEvent event) -> {
deleteRow(grid, formatter.getValue());
});
for (int r = 0; r < 5; r++) {
for (int c = 0; c < 3; c++) {
grid.add(new Text(r+"_"+c), c, r);
}
}
Scene scene = new Scene(new VBox(new HBox(tf, btn), grid));
primaryStage.setScene(scene);
primaryStage.show();
}
static void deleteRow(GridPane grid, final int row) {
Set<Node> deleteNodes = new HashSet<>();
for (Node child : grid.getChildren()) {
// get index from child
Integer rowIndex = GridPane.getRowIndex(child);
// handle null values for index=0
int r = rowIndex == null ? 0 : rowIndex;
if (r > row) {
// decrement rows for rows after the deleted row
GridPane.setRowIndex(child, r-1);
} else if (r == row) {
// collect matching rows for deletion
deleteNodes.add(child);
}
}
// remove nodes from row
grid.getChildren().removeAll(deleteNodes);
}