Reputation: 314
I've got following problem:
I styled a TableViews Cells into Rectangles. Now I want to add Objects with an X and Y Coordinate in it (obj.getAblaufX()
and obj.getAblaufY()
).
How can I add them correctly in JavaFX TableView?
My Current Code:
ObservableList<Event> eventList = loadEvents();
TableView<Event> tableView = new TableView<Event>();
tableView.getSelectionModel().setCellSelectionEnabled(true);
tableView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
tableView.setItems(eventList);
for(int i = 0; i < 10; i++) {
TableColumn<Event, String> tmpCol = new TableColumn<>();
tmpCol.setId(Integer.toString(i));
tmpCol.setMaxWidth(40);
tmpCol.setCellValueFactory(cellData -> new ObservableValue<String>() {
@Override
public void addListener(InvalidationListener listener) {
// TODO Auto-generated method stub
}
@Override
public void removeListener(InvalidationListener listener) {
// TODO Auto-generated method stub
}
@Override
public void addListener(ChangeListener<? super String> listener) {
// TODO Auto-generated method stub
}
@Override
public void removeListener(ChangeListener<? super String> listener) {
// TODO Auto-generated method stub
}
@Override
public String getValue() {
if(Integer.parseInt(tmpCol.getId()) == cellData.getValue().getAblaufX()) {
return cellData.getValue().getAbteilung();
} else {
return "";
}
}
});
tableView.getColumns().add(tmpCol);
}
Result:
But:
Object SP1 has X = 0 and Y = 0
Object SP2 has X = 1 and Y = 0
So, how to fix this Problem?
Upvotes: 1
Views: 972
Reputation: 82461
By using Event
as item type, you pervent the TableView
from displaying more than one Event
per row. Use Integer
as item type instead and store the values in a ObservableMap<TablePos, Event>
where TablePos
is a class containing the x
and y
coordinates:
public final class TablePos {
private final int x;
private final int y;
public TablePos(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public int hashCode() {
int hash = 7;
hash = 17 * hash + this.x;
hash = 17 * hash + this.y;
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof TablePos)) {
return false;
}
final TablePos other = (TablePos) obj;
return (this.x == other.x && this.y == other.y);
}
}
public class Event {
private final StringProperty name;
private final IntegerProperty x;
private final IntegerProperty y;
public Event(String name, int x, int y) {
// set beans for properties here to make this instance accessible to listeners
this.y = new SimpleIntegerProperty(this, "y", y);
this.x = new SimpleIntegerProperty(this, "x", x);
this.name = new SimpleStringProperty(this, "name", name);
}
public final String getName() {
return this.name.get();
}
public final void setName(String value) {
this.name.set(value);
}
public final StringProperty nameProperty() {
return this.name;
}
public final int getX() {
return this.x.get();
}
public final void setX(int value) {
this.x.set(value);
}
public final IntegerProperty xProperty() {
return this.x;
}
public final int getY() {
return this.y.get();
}
public final void setY(int value) {
this.y.set(value);
}
public final IntegerProperty yProperty() {
return this.y;
}
}
private static void put(Map<TablePos, Event> map, Event evt) {
map.put(new TablePos(evt.getX(), evt.getY()), evt);
}
@Override
public void start(Stage primaryStage) {
ObservableMap<TablePos, Event> contents = FXCollections.observableHashMap();
TableView<Integer> tableView = new TableView<>(FXCollections.observableArrayList());
TableColumn columnGroup = new TableColumn("Heutige Termine");
ChangeListener<Number> xChangeListener = (observable, oldValue, newValue) -> {
Event evt = (Event) ((Property) observable).getBean();
TablePos oldPos = new TablePos(oldValue.intValue(), evt.getY());
TablePos newPos = new TablePos(newValue.intValue(), evt.getY());
contents.remove(oldPos);
contents.put(newPos, evt);
};
ChangeListener<Number> yChangeListener = (observable, oldValue, newValue) -> {
Event evt = (Event) ((Property) observable).getBean();
TablePos oldPos = new TablePos(evt.getX(), oldValue.intValue());
TablePos newPos = new TablePos(evt.getX(), newValue.intValue());
contents.remove(oldPos);
contents.put(newPos, evt);
};
contents.addListener((MapChangeListener.Change<? extends TablePos, ? extends Event> change) -> {
if (change.wasRemoved()) {
Event evt = change.getValueRemoved();
evt.xProperty().removeListener(xChangeListener);
evt.yProperty().removeListener(yChangeListener);
}
if (change.wasAdded()) {
Event evt = change.getValueAdded();
evt.xProperty().addListener(xChangeListener);
evt.yProperty().addListener(yChangeListener);
}
});
// items denote the y coordinate
for (int i = 0; i < 10; i++) {
tableView.getItems().add(i);
}
// one column per x coordiante
for (int i = 0; i < 10; i++) {
final int index = i;
TableColumn<Integer, String> column = new TableColumn<>();
// take value from Map using x (index) and y (item value)
column.setCellValueFactory(cd
-> Bindings.selectString(
Bindings.valueAt(contents, new TablePos(index, cd.getValue())), "name"));
columnGroup.getColumns().add(column);
}
tableView.getColumns().add(columnGroup);
Event opTarget = new Event("Something", 2, 9);
put(contents, new Event("SP1", 0, 0));
put(contents, new Event("SP2", 1, 0));
put(contents, opTarget);
Button move = new Button("move");
move.setOnAction(evt -> opTarget.setX(9 - opTarget.getX()));
Button rename = new Button("rename");
rename.setOnAction(evt -> opTarget.setName(opTarget.getName().equals("42") ? "Answer" : "42"));
Scene scene = new Scene(new VBox(10, tableView, move, rename));
primaryStage.setScene(scene);
primaryStage.show();
}
Note that this requires you to know the number of rows/columns at the initialisation (I used 10 for both for simplicity). If this is not the case, you could add a listener to the ObservableMap
that dynamically adds/removes rows / columns.
Upvotes: 1