JavaFX - bug in cell render after hiding columns - javafx

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.

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 Multiple cellValueFactories for one tableRow?

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);
}
}
});

javaFX tableview can not correct render row

if I add more items into table until it generate a scroll bar. At this time, when I scrolled scroll bar, first name not equal "Jacob" was rendered yellow.
enter image description here
table.setRowFactory(row -> new TableRow<Person>() {
#Override
public void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
} else {
if (item.getFirstName().equals("Jacob")) {
setStyle("-fx-background-color: yellow");
}
}
}
});
When you scroll, the rows are reused to represent new items that are displayed. Your row factory never removes a style once it is set, so any rows that were yellow remain yellow no matter which items they are reused for.
In other words, once a particular row displays a person with first name "Jacob", that row will turn yellow, and then the style will never change (it will always stay yellow), even if the row is reused to display another person.
You need to reset the style if it the row should not be yellow:
table.setRowFactory(row -> new TableRow<Person>() {
#Override
public void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setStyle("");
} else {
if (item.getFirstName().equals("Jacob")) {
setStyle("-fx-background-color: yellow");
} else {
setStyle("");
}
}
}
});

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.

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