Reputation: 2961
I need to change the table row color using a property that would not be visible in any column of a tableview
. I did the following:
Person
(serialNumber
, first
, last
).Person
using an extractor.tableview1
, tableview2
) and one listview
that all sharing the same data.tableview1
has a serialCol1
column with a visible property set to
false.I want to change tableview1
row color using the serialNumber
property that is bound to a column in a tableview2
.
Here is the complete program:
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
/**
*
* @author kachna
*/
public class Extractor extends Application {
private final TableView<Person> tableView1 = new TableView<>();
private final TableView<Person> tableView2 = new TableView<>();
private final ListView<Person> listView = new ListView<>();
//observable list with extractor
private final ObservableList<Person> data = FXCollections.observableArrayList(p -> new Observable[]{p.serialNumberProperty(), p.firstProperty(), p.lastProperty()});
static class Person {
final IntegerProperty serialNumber;
final StringProperty first;
final StringProperty last;
public Person(int serialNumber, String first, String last) {
this.first = new SimpleStringProperty(first);
this.last = new SimpleStringProperty(last);
this.serialNumber = new SimpleIntegerProperty(serialNumber);
}
public IntegerProperty serialNumberProperty() {
return serialNumber;
}
public StringProperty firstProperty() {
return first;
}
public StringProperty lastProperty() {
return last;
}
@Override
public String toString() {
return "Person{" + "first=" + first.get() + ", last=" + last.get() + '}';
}
}
@Override
public void start(Stage stage) {
BorderPane root = new BorderPane();
VBox vBox = new VBox(10);
VBox.setVgrow(tableView2, Priority.ALWAYS);
root.setPadding(new Insets(10));
initTableViews();
initListView();
getData();
Label label1 = new Label("TableView 1");
label1.setStyle("-fx-font-size: 24px;\n"
+ "-fx-font-weight: bold;");
Label label2 = new Label("TableView 2");
label2.setStyle("-fx-font-size: 24px;\n"
+ "-fx-font-weight: bold;");
vBox.getChildren().addAll(label1, tableView1,label2, tableView2);
root.setCenter(vBox);
root.setRight(listView);
Scene scene = new Scene(root, 600, 400);
stage.setScene(scene);
stage.show();
}
private void initTableViews() {
// first table view
tableView1.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
tableView1.setEditable(true);
tableView1.setRowFactory(tv -> new TableRow<Person>() {
@Override
protected void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
if (item.serialNumber.get() % 2 == 0) {
setStyle("-fx-background-color: orange;");
} else {
setStyle(" ");
}
} else {
setStyle(" ");
}
}
});
TableColumn<Person, Number> serialCol1 = new TableColumn<>("Serial Number");
serialCol1.setCellValueFactory(cellData -> cellData.getValue().serialNumberProperty());
serialCol1.setCellFactory(TextFieldTableCell.forTableColumn(new StringConverter<Number>() {
@Override
public String toString(Number object) {
return object.toString();
}
@Override
public Number fromString(String string) {
return Integer.parseInt(string);
}
}));
// make the serialCol1 column invisible
serialCol1.setVisible(false);
TableColumn<Person, String> firstCol1 = new TableColumn<>("First Name");
firstCol1.setCellValueFactory(cellData -> cellData.getValue().firstProperty());
firstCol1.setCellFactory(TextFieldTableCell.forTableColumn());
TableColumn<Person, String> lastCol1 = new TableColumn<>("Last Name");
lastCol1.setCellFactory(TextFieldTableCell.forTableColumn());
lastCol1.setCellValueFactory(cellData -> cellData.getValue().lastProperty());
tableView1.getColumns().addAll(serialCol1, firstCol1, lastCol1);
tableView1.setItems(data);
// second table view
tableView2.setEditable(true);
tableView2.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
TableColumn<Person, Number> serialCol = new TableColumn<>("Serial Number");
serialCol.setCellValueFactory(cellData -> cellData.getValue().serialNumberProperty());
serialCol.setCellFactory(TextFieldTableCell.forTableColumn(new StringConverter<Number>() {
@Override
public String toString(Number object) {
return object.toString();
}
@Override
public Number fromString(String string) {
return Integer.parseInt(string);
}
}));
TableColumn<Person, String> firstCol2 = new TableColumn<>("First Name");
firstCol2.setCellValueFactory(cellData -> cellData.getValue().firstProperty());
TableColumn<Person, String> lastCol2 = new TableColumn<>("Last Name");
lastCol2.setCellFactory(TextFieldTableCell.forTableColumn());
lastCol2.setCellValueFactory(cellData -> cellData.getValue().lastProperty());
tableView2.getColumns().addAll(serialCol, firstCol2, lastCol2);
tableView2.setItems(data);
}
private void initListView() {
//list view
listView.setCellFactory(list -> new ListCell<Person>() {
@Override
protected void updateItem(Person value, boolean empty) {
super.updateItem(value, empty);
if (!empty && value != null) {
if (value.serialNumber.get() % 2 == 0) {
setStyle("-fx-background-color: orange;");
} else {
setStyle(" ");
}
setText(String.format("%s %s %s", value.serialNumber.get(), value.firstProperty().get(), value.lastProperty().get()));
} else {
setText(null);
setStyle(" ");
}
}
});
listView.setItems(data);
}
private void getData() {
data.setAll(IntStream.range(0, 10)
.mapToObj(i -> new Person(i, "first" + i, "last" + i))
.collect(Collectors.toList()));
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Problem:
tableview1
doesn't change instantly after applying a
change. I have to scroll hrough the rows to see the style updated. the style of the listview
is changed instantly without any problems.Upvotes: 0
Views: 876
Reputation: 1725
The updateItem
method is not bound to the property lifecycle of its item ( an item must not be an Observable ), but rather gets called by the View (ListView/TableView) whenever it deems it necessary to update the data representation. When you scroll a Row off screen it gets nulled ( I assume for performance reasons ) and updated again when in screen.
What you want to do is to bind the styleProperty
of the row to its items serialNumberProperty
like so:
tableView1.setRowFactory( tv -> new TableRow<Person>()
{
@Override
protected void updateItem( final Person item, final boolean empty )
{
super.updateItem( item, empty );
if ( !empty && item != null )
{
this.styleProperty().bind( Bindings.createStringBinding( () ->
{
if ( item.serialNumber.get() % 2 == 0 )
{
return "-fx-background-color: orange;";
}
return " ";
} , item.serialNumberProperty() ) );
}
else
{
/*
* As per comment in the Cell API
*/
setText( null );
setGraphic( null );
this.styleProperty().unbind();
setStyle( " " );
}
}
} );
I also recommend consulting the documentation of javafx.scene.control.Cell#updateitem(...)
as it is marked as "Expert API".
Upvotes: 3