JavaFX Multiple cellValueFactories for one tableRow? - javafx

I'm newish to JavaFX still and don't quite understand how factories work.
What I'm trying to do is both apply styling to a tableCell in a tableColumn depending on what the String value is, and also make the cell editable.
Right now my code looks like this:
notesColumn.setCellFactory(column -> new TableCell<Computer, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null | empty)
{
setText(null);
setStyle("");
}
else
{
setText(item);
if (item.contains("Restoring #")) setTextFill(Color.CRIMSON);
else if (item.contains("Ready")) setTextFill(Color.FORESTGREEN);
else setTextFill(Color.BLACK);
}
}
});
And this piece works as I'd like it to, but if I try to make the cell editable by using the below code, the cellFactories overwrite one another, of course. How do I combine the two?
notesColumn.setCellFactory(TextFieldTableCell.forTableColumn());

The following should work:
notesColumn.setCellFactory(column -> new TextFieldTableCell<Computer, String>(new DefaultStringConverter()) {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null | empty)
{
setStyle("");
}
else
{
if (item.contains("Restoring #")) setTextFill(Color.CRIMSON);
else if (item.contains("Ready")) setTextFill(Color.FORESTGREEN);
else setTextFill(Color.BLACK);
}
}
});

Related

JavaFX ListView mirroring content at the end

When I add new item to my ListView, it starts mirroring all the content at the end it's layout (it's not clickable) does somebody know what can cause it? Thanks.
Ok, I found the problem.
I have the list of objects and want it to display the title of object as list node text.
The problem is that I wrongly defined the custom cell factory.
tagListView.setCellFactory(param -> new ListCell<>() {
#Override
protected void updateItem(StoreTagVoImpl item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setText(item.getTitle());
}
}
});
But it should be as this:
tagListView.setCellFactory(param -> new ListCell<>() {
#Override
protected void updateItem(StoreTagVoImpl item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(item.getTitle());
}
}
});

JavaFX TableView scrolling colors different row that aren't ment to be colored

Forum,
I have created a Tableview. Every row is filled with the information given by each "Steckerpin"-object. Using RowFactory I color the backround of each row depending on the value of the attribute "Zustand" of every object ("Steckerpin").
The problem is, when the table becomes scrollable, the new appearing row are coloured in a similar order as the colored rows in the beginning. But those rows shouldn't be colored, because their attribute "Zustand" hasn't changed.
Colored rows before scrolling
New appearing rows after scrolling
If I scroll to the end of the table, the wrong colored rows become white again.
I have read that it is a common problem using tableviews in combination with RowFactory, because the rows are reused once you start scrolling.
Here is my code using the RowFactory:
durchgangsTable.setRowFactory(tv -> new TableRow<Steckerpin>() {
#Override
public void updateItem(Steckerpin item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
setStyle("");
} else if (item.getZustand().equals(okZustString)) {
pseudoClassStateChanged(PseudoClass.getPseudoClass("ok"), true);
} else if (item.getZustand().equals(fehlerZustString)) {
pseudoClassStateChanged(PseudoClass.getPseudoClass("error"), true);
} else if (item.getZustand().equals(prüfZustString)) {
pseudoClassStateChanged(PseudoClass.getPseudoClass("pending"), true);
}
}
});
I have read a couple of similar questions, but I can't find a solution for my problem. So I would be very grateful for some help.
When you scroll, existing rows are reused to display new items. So, for example, if you had a row which displayed an item which was in an "error" state, and it is reused for an item which is in an "ok" state, then both pseudoclasses will be set to true.
Consequently, you need to set all pseudoclasses to the correct state under all conditions:
durchgangsTable.setRowFactory(tv -> new TableRow<Steckerpin>() {
#Override
public void updateItem(Steckerpin item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
setStyle("");
pseudoClassStateChanged(PseudoClass.getPseudoClass("ok"), false);
pseudoClassStateChanged(PseudoClass.getPseudoClass("error"), false);
pseudoClassStateChanged(PseudoClass.getPseudoClass("pending"), false);
} else if (item.getZustand().equals(okZustString)) {
pseudoClassStateChanged(PseudoClass.getPseudoClass("ok"), true);
pseudoClassStateChanged(PseudoClass.getPseudoClass("error"), false);
pseudoClassStateChanged(PseudoClass.getPseudoClass("pending"), false);
} else if (item.getZustand().equals(fehlerZustString)) {
pseudoClassStateChanged(PseudoClass.getPseudoClass("ok"), false);
pseudoClassStateChanged(PseudoClass.getPseudoClass("error"), true);
pseudoClassStateChanged(PseudoClass.getPseudoClass("pending"), false);
} else if (item.getZustand().equals(prüfZustString)) {
pseudoClassStateChanged(PseudoClass.getPseudoClass("ok"), false);
pseudoClassStateChanged(PseudoClass.getPseudoClass("error"), false);
pseudoClassStateChanged(PseudoClass.getPseudoClass("pending"), true);
} else {
pseudoClassStateChanged(PseudoClass.getPseudoClass("ok"), false);
pseudoClassStateChanged(PseudoClass.getPseudoClass("error"), false);
pseudoClassStateChanged(PseudoClass.getPseudoClass("pending"), false);
}
}
});
Equivalently, with less code:
durchgangsTable.setRowFactory(tv -> new TableRow<Steckerpin>() {
private final List<String> states = List.of("ok", "error", "pending");
#Override
public void updateItem(Steckerpin item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
setStyle("");
updateState(null);
} else if (item.getZustand().equals(okZustString)) {
updateState("ok");
} else if (item.getZustand().equals(fehlerZustString)) {
updateState("error");
} else if (item.getZustand().equals(prüfZustString)) {
updateState("pending");
} else {
updateState(null);
}
}
private void updateState(String newState) {
states.forEach(state ->
pseudoClassStateChanged(PseudoClass.getPseudoClass(state), state.equals(newState)));
}
});

How to force TableRow repaint

How to force TableRow repaint ?.
Imagine the following scenario: The tableView is updated every 180 milliseconds, but the cell that receives the TableRow style information is not visible, and every time it is upgraded TableRow needs to be repainted. When I use refresh() method, it does not look good, especially with the mouse positioning on the TableView, it blinks and in this case consuming cpu.
myTableView.setRowFactory( new Callback<TableView, TableRow<Line>>() {
#Override
public TableRow call(final TableView p) {
return new TableRow<Line>() {
#Override
public void updateItem(Line item, boolean empty) {
super.updateItem(item, empty);
if(item != null) {
if(item.statusProperty().getValue().equals("BORDER")) {
setStyle("-fx-border-color:green;-fx-border-width:2;-fx-opacity:1;");
}
}
}
};
}
});
Since the style depends on the statusProperty() of the Line, which is observable, you can use a binding:
#Override
public void updateItem(Line item, boolean empty) {
super.updateItem(item, empty);
if(item != null) {
styleProperty().bind(Bindings
.when(item.statusProperty().isEqualTo("BORDER"))
.then("-fx-border-color:green;-fx-border-width:2;-fx-opacity:1;")
.otherwise(""));
} else {
styleProperty().unbind();
setStyle("");
}
}
An alternative way to create the binding, which is probably more convenient if the logic is more complicated, is
#Override
public void updateItem(Line item, boolean empty) {
super.updateItem(item, empty);
if(item != null) {
styleProperty().bind(Bindings.createStringBinding(() -> {
if ("BORDER".equals(item.getStyle())) {
return "-fx-border-color:green;-fx-border-width:2;-fx-opacity:1;" ;
} else {
return "" ;
}
}, item.statusProperty());
} else {
styleProperty().unbind();
setStyle("");
}
}
This way the table row will observe the current item's status property, and automatically update the style if that property changes.
If you really want to make the code cleaner, of course, you should move the styles to an external CSS file. You can create a CSS PseudoClass (or more than one) which you can set and unset on the row:
final PseudoClass borderPC = PseudoClass.getPseudoClass("border");
myTableView.setRowFactory(p -> {
TableRow<Line> row = new TableRow<>();
ChangeListener<String> statusListener = (obs, oldStatus, newStatus) ->
row.pseudoClassStateChanged(borderPC, "BORDER".equals(newStatus)) ;
row.itemProperty().addListener((obs, oldLine, newLine) -> {
if (oldLine != null) {
oldLine.statusProperty().removeListener(statusListener);
}
if (newLine == null) {
row.pseudoClassStateChanged(borderPC, false);
} else {
newLine.statusProperty().addListener(statusListener);
row.pseudoClassStateChanged(borderPC, "BORDER".equals(newLine.getStatus()));
}
};
return row ;
});
Then in your external CSS file, do
.table-row-cell:border {
-fx-border-color:green;
-fx-border-width:2;
-fx-opacity:1;
}
Again, you can easily add more psuedoclasses, more rules to the CSS, and additional tests and pseudoclass updates using this approach.

JavaFX - bug in cell render after hiding columns

First of all sorry for my English:)
I have a strange bug... I'd like to hide some columns in JavaFX TableView after pressing the button. The code is as simple as possible:
column8.visibleProperty().set(false);
column9.visibleProperty().set(false);
(I also tested with remove with the same result).
The problem is that in "neighboring cell" I have "separator" from somewhere after this action.
I use Cellfactories is my code. For one of the hidden Cell it is
column8.setCellFactory(column -> {
return new TableCell<Anfrage, Mandant>() {
#Override
protected void updateItem(Mandant item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(item.getNameMandant());
}
}
};
});
And for cell, where I have "separator" after hiding:
//Store
column10.setCellFactory(column -> {
return new TableCell<Anfrage, Terminals>() {
#Override
protected void updateItem(Terminals item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(item.getStore());
}
}
};
});
Some pics to explain what is actually wrong.
If i set visible to "true" again everythings works as expected - all columns are in there without any "separators" anywhere.
Thanks a lot for any advice!
So, finally tableview refreshing is resolved in JavaFX 8u60. Now "native" table.refresh() do the trick.

Colouring table row in JavaFX

This question is related to this. Now I want to colour the row where field value equals to some value.
#FXML
private TableView<FaDeal> tv_mm_view;
#FXML
private TableColumn<FaDeal, String> tc_inst;
tc_inst.setCellValueFactory(cellData -> new SimpleStringProperty(""+cellData.getValue().getInstrumentId()));
tc_inst.setCellFactory(column -> new TableCell<FaDeal, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(item);
// Style row where balance < 0 with a different color.
TableRow currentRow = getTableRow();
if (item.equals("1070")) {
currentRow.setStyle("-fx-background-color: tomato;");
} else currentRow.setStyle("");
}
}
});
The problem is I don't want to show tc_inst in my table. For this reason I set visible checkbox in SceneBuilder to false. In this case colouring part doesn't work at all. How can hide tc_inst so that colouring works?
Use a row factory, instead of a cell factory, if you want to change the color of the whole row:
tv_mm_view.setRowFactory(tv -> new TableRow<FaDeal>() {
#Override
public void updateItem(FaDeal item, boolean empty) {
super.updateItem(item, empty) ;
if (item == null) {
setStyle("");
} else if (item.getInstrumentId().equals("1070")) {
setStyle("-fx-background-color: tomato;");
} else {
setStyle("");
}
}
});
Note that if the value of instrumentId changes while the row is displayed, then the color will not change automatically with the above code, unless you do some additional work. The simplest way to make that happen would be to construct your items list with an extractor which returned the instrumentIdProperty() (assuming you are using the JavaFX property pattern in FaDeal).

Resources