Highlighting a row in table view in javafx [duplicate] - javafx

I need to add color to row and column based on pnl value. How can I change the color of the row and how do I get pnl value for determining change in the color of the row.
TreeTableColumn<ClosedTradesPnL, String> symColumn = new TreeTableColumn<>("Symbol");
symColumn.setPrefWidth(100);
symColumn.setCellValueFactory(
(TreeTableColumn.CellDataFeatures<ClosedTradesPnL, String> param) ->
new ReadOnlyStringWrapper(param.getValue().getValue().getSymbol())
);
TreeTableColumn<ClosedTradesPnL, Date> expiryColumn =
new TreeTableColumn<>("Expiry Date");
expiryColumn.setPrefWidth(100);
expiryColumn.setCellValueFactory(
(TreeTableColumn.CellDataFeatures<ClosedTradesPnL, Date> param) ->
new ReadOnlyObjectWrapper(param.getValue().getValue().getExpiry_date())
);
TreeTableColumn<ClosedTradesPnL, String> pnlColumn =
new TreeTableColumn<>("PnL");
pnlColumn.setPrefWidth(100);
// pnlColumn.setStyle(" -fx-background-color: red ;");
// pnlColumn.setCellValueFactory(
// (TreeTableColumn.CellDataFeatures<ClosedTradesPnL, String> param) ->
// new ReadOnlyStringWrapper(param.getValue().getValue().getRealized_PNL())
// );
pnlColumn.setCellValueFactory(new Callback<CellDataFeatures<ClosedTradesPnL, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<ClosedTradesPnL, String> p) {
int foo = Integer.parseInt(p.getValue().getValue().getRealized_PNL().replace(",", "").replace(".", ""));
if( foo == 0){
System.out.println("color app"+p.getValue().getValue().getRealized_PNL());
pnlColumn.setStyle(" -fx-background-color: red ;");
}else{
pnlColumn.setStyle("-fx-background-color: white ;");
}
System.out.println(p.getValue().getValue().getRealized_PNL());
return new ReadOnlyObjectWrapper(p.getValue().getValue().getRealized_PNL());
}
});
TreeTableView<ClosedTradesPnL> treeTableView = new TreeTableView<>(root);
treeTableView.getColumns().setAll(symColumn, expiryColumn,pnlColumn);
// pnlColumn.setStyle("-fx-alignment: center-right;-fx-control-inner-background: slateblue;");
sceneRoot.getChildren().add(treeTableView);
// if (treeTableView.getRow(root)){treeTableView.setBackground(Background.RED);}
stage.setScene(scene);
stage.show();
}

This should be self explanatory, I've included a few comments.
import java.util.Date;
import javafx.application.Application;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableRow;
import javafx.scene.control.TreeTableView;
import javafx.stage.Stage;
public class StyleTableRow extends Application {
#Override
public void start(Stage primaryStage) {
TreeTableView<ClosedTradesPnL> ttv = new TreeTableView<>(new TreeItem<>(new ClosedTradesPnL()));
for (int i=0;i<10;i++) ttv.getRoot().getChildren().add(new TreeItem<>(new ClosedTradesPnL()));
ttv.getRoot().setExpanded(true);
TreeTableColumn<ClosedTradesPnL, String> symColumn = new TreeTableColumn<>("Symbol");
symColumn.setCellValueFactory((param) ->
new ReadOnlyStringWrapper(param.getValue().getValue().getSymbol())
);
TreeTableColumn<ClosedTradesPnL, Date> expiryColumn = new TreeTableColumn<>("Expiry Date");
expiryColumn.setCellValueFactory((param) ->
new ReadOnlyObjectWrapper<>(param.getValue().getValue().getExpiry_date())
);
//use Number for type of data in column
TreeTableColumn<ClosedTradesPnL, Number> pnlColumn = new TreeTableColumn<>("PnL");
pnlColumn.setCellValueFactory((param) ->
new ReadOnlyDoubleWrapper(param.getValue().getValue().getRealized_PNL())
);
//now use a cellFactory to style the cell
//you can get the row and style it as well
pnlColumn.setCellFactory((TreeTableColumn<ClosedTradesPnL, Number> param) -> {
TreeTableCell cell = new TreeTableCell<ClosedTradesPnL, Number>(){
#Override
//by using Number we don't have to parse a String
protected void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
TreeTableRow<ClosedTradesPnL> ttr = getTreeTableRow();
if (item == null || empty){
setText(null);
ttr.setStyle("");
setStyle("");
} else {
ttr.setStyle(item.doubleValue() > 0
? "-fx-background-color:lightgreen"
: "-fx-background-color:pink");
setText(item.toString());
setStyle(item.doubleValue() > 0
? "-fx-background-color:green"
: "-fx-background-color:red");
}
}
};
return cell;
});
ttv.getColumns().setAll(symColumn, expiryColumn,pnlColumn);
Scene scene = new Scene(ttv, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private static class ClosedTradesPnL{
private SimpleStringProperty symbol = new SimpleStringProperty("symbol");
private SimpleObjectProperty<Date> expiry_date = new SimpleObjectProperty<>(new Date(System.currentTimeMillis()));
private SimpleDoubleProperty realized_PNL= new SimpleDoubleProperty(Math.random()-0.5);
public String getSymbol() {return symbol.get();}
public Date getExpiry_date() {return expiry_date.get();}
public double getRealized_PNL() {return realized_PNL.get();}
}
}

Related

Howto select and edit TabelCell in TableView with mouseclick on a MenuItem of ContextMenu - JavaFX

is there a possiblity to select & edit a TableCell within a TableView after one has mouse clicked or key pressed a certain MenuItem of a ContextMenu? The default TextFieldTableCell Implementation only supports pressing Enter key while a TableRow has focus or directly clicking into the respective cell to select & edit its content.
If understand your question correctly, yes you can do this. Here's an example that lets you edit cells in the second column by using a context menu:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.stage.Stage;
public class EditableTableMCVE extends Application {
#Override
public void start(Stage stage) {
int numOfCols = 10;
ObservableList<ObservableList<String>> tableData = FXCollections.observableArrayList();
// Generate dummy data.
for (int i = 0; i < 100; i++) {
ObservableList<String> row = FXCollections.observableArrayList();
for (int j = 0; j < numOfCols; j++)
row.add("Row" + i + "Col" + j);
tableData.add(row);
}
TableView<ObservableList<String>> table = new TableView<ObservableList<String>>();
table.setEditable(true);
// Add columns to the table.
for (int i = 0; i < numOfCols; i++) {
// We make all cells in column on index 1 editable.
if (i == 1) {
table.getColumns().add(addEditableColumn(i, "Column " + i));
} else {
table.getColumns().add(addColumn(i, "Column " + i));
}
}
table.getItems().addAll(tableData);
MenuItem editItem = new MenuItem("Edit");
editItem.setOnAction(e -> {
// We need to get the index of the selected row and the TableColumn
// on the column index we want to edit.
int selectedRowIndex = table.getSelectionModel().getSelectedIndex();
table.edit(selectedRowIndex, table.getColumns().get(1));
});
ContextMenu menu = new ContextMenu(editItem);
table.setContextMenu(menu);
Scene scene = new Scene(table);
stage.setScene(scene);
stage.show();
}
/**
* Returns a simple column.
*/
private TableColumn<ObservableList<String>, String> addColumn(int index, String name) {
TableColumn<ObservableList<String>, String> col = new TableColumn<ObservableList<String>, String>(name);
col.setCellValueFactory(e -> new SimpleStringProperty(e.getValue().get(index)));
return col;
}
/**
* Returns an editable column.
*/
private TableColumn<ObservableList<String>, String> addEditableColumn(int index, String name) {
TableColumn<ObservableList<String>, String> col = new TableColumn<ObservableList<String>, String>(name);
col.setCellValueFactory(e -> new SimpleStringProperty(e.getValue().get(index)));
col.setCellFactory(TextFieldTableCell.forTableColumn());
col.setOnEditCommit(new EventHandler<CellEditEvent<ObservableList<String>, String>>() {
#Override
public void handle(CellEditEvent<ObservableList<String>, String> t) {
((ObservableList<String>) t.getTableView().getItems().get(t.getTablePosition().getRow())).set(index,
t.getNewValue());
}
});
return col;
}
public static void main(String[] args) {
launch();
}
}
You can do this by extending TextFieldTableCell.
Example
public class ContextTextFieldTableCell<S, T> extends TextFieldTableCell<S, T> {
private void init() {
MenuItem editItem = new MenuItem("edit");
ContextMenu contextMenu = new ContextMenu(editItem);
setContextMenu(contextMenu);
setEditable(false);
setOnContextMenuRequested(evt -> {
editItem.setDisable(!(getTableColumn().isEditable() && getTableView().isEditable()));
});
editItem.setOnAction(evt -> {
setEditable(true);
getTableView().edit(getIndex(), getTableColumn());
});
}
public ContextTextFieldTableCell() {
}
public ContextTextFieldTableCell(StringConverter<T> converter) {
super(converter);
init();
}
#Override
public void cancelEdit() {
super.cancelEdit();
setEditable(false);
}
#Override
public void commitEdit(T newValue) {
super.commitEdit(newValue);
setEditable(false);
}
public static <T> Callback<TableColumn<T, String>, TableCell<T, String>> cellFactory() {
return cellFactory(new DefaultStringConverter());
}
public static <S, T> Callback<TableColumn<S, T>, TableCell<S, T>> cellFactory(final StringConverter<T> converter) {
if (converter == null) {
throw new IllegalArgumentException();
}
return column -> new ContextTextFieldTableCell<>(converter);
}
}
#Override
public void start(Stage primaryStage) {
TableView<Item<String>> tableView = createTable();
tableView.setEditable(true);
TableColumn<Item<String>, String> editColumn = new TableColumn("value(editable)");
editColumn.setCellFactory(ContextTextFieldTableCell.cellFactory());
editColumn.setCellValueFactory(cd -> cd.getValue().valueProperty());
tableView.getColumns().add(editColumn);
Scene scene = new Scene(tableView);
primaryStage.setScene(scene);
primaryStage.show();
}
Note that this removes the "standard" way of entering the edit mode. If you do not need this functionality, you can simply use a cellFactory to customize TextFieldTableCell:
public static <S, T> Callback<TableColumn<S, T>, TableCell<S, T>> cellFactory(final StringConverter<T> converter) {
if (converter == null) {
throw new IllegalArgumentException();
}
return column -> {
final TextFieldTableCell<S, T> cell = new TextFieldTableCell<>(converter);
MenuItem editItem = new MenuItem("edit");
ContextMenu contextMenu = new ContextMenu(editItem);
cell.setContextMenu(contextMenu);
cell.setOnContextMenuRequested(evt -> {
editItem.setDisable(!(cell.isEditable() && cell.getTableColumn().isEditable() && cell.getTableView().isEditable()));
});
editItem.setOnAction(evt -> {
cell.getTableView().edit(cell.getIndex(), cell.getTableColumn());
});
return cell;
};
}

javafx TableView how to edit a TextField on external TextArea control and return the result to the field properly

I have been trying to accomplish editing of a cell in TableView which has long text on it. for that I have to pass the value to an external window TextArea and return the result to the field, but I can not get the value changed but after pushing ENTER and then select a new row with the mouse. The keyboard gets frozen. If I click same row the field doe snot change
My code is
contact_view.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue obs, Object oldSelection, Object newSelection) {
if (newSelection != null) {
TableView.TableViewSelectionModel selectionModel = contact_view.getSelectionModel();
ObservableList selectedCells = selectionModel.getSelectedCells();
tablePosition = (TablePosition) selectedCells.get(0);
row = tablePosition.getRow();
col = tablePosition.getColumn();
Object colname = tablePosition.getTableColumn().getId();
val = (String) notes.getCellData(row);
String tmp="";
tmp = String.valueOf(cntac_name.getCellData(row));
frozenCol.setText(tmp);
System.out.println("Hola mundo 563 "+" "+row +" "+col+" "+val+" "+colname+" "+tmp);
// frozenCol.setText((java.lang.String) val);
if(col == 21) {
notes.setCellFactory(TextFieldTableCell.<Contact>forTableColumn());
notes.setOnEditStart(new EventHandler<CellEditEvent<Contact, String>>() {
#Override
public void handle(CellEditEvent<Contact, String> t) {
((Contact) t.getTableView().getItems().get(t.getTablePosition().getRow())).setNotes(t.getNewValue());
String cscode =t.getTableView().getItems().get(t.getTablePosition().getRow()).cstmr_code;
String newValue =t.getNewValue();
// notes.setText((java.lang.String) newValue);
System.out.println("Hola mundo 615 "+t.getNewValue()+" "+t.getTableView().getItems().get(t.getTablePosition().getRow()).cstmr_code);
String ok=String.valueOf(cssEditorFld.getText());
System.out.println("Hola mundo 617 "+" "+row +" "+col+" "+val+" "+cntac_name.getCellData(row)+" "+cssEditorFld.getText()+" "+ok);
// boolean r = CstmersDBConnection.UpdateSingleContact(cscode, cntac_code, colname, newValue);
//setCellFactory(SetEditorTxtArea());
Stage s=new Stage();
Object p =cntac_name.getCellData(row);
s.initModality(Modality.APPLICATION_MODAL);
s.initStyle(StageStyle.DECORATED);
s.setResizable(true);
s.setTitle(String.valueOf(p));
Group root = new Group();
GridPane gridpane = new GridPane();
gridpane.setPadding(new Insets(1));
gridpane.setHgap(1);
gridpane.setVgap(1);
cssEditorFld.setText(String.valueOf(val));
cssEditorFld.setPrefRowCount(20);
cssEditorFld.setPrefColumnCount(150);
cssEditorFld.setWrapText(true);
cssEditorFld.setPrefWidth(300);
GridPane.setHalignment(cssEditorFld, HPos.CENTER);
gridpane.add(cssEditorFld, 0, 1);
HBox hbox = addHBox();
gridpane.add(hbox, 0, 2);
root.getChildren().add(gridpane);
s.setScene(new Scene(root));
s.show();
buttonYs.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
System.out.println(" Clicked Yes");
String ok=String.valueOf(cssEditorFld.getText());
n= true;
s.hide();
((Contact) t.getTableView().getItems().get(t.getTablePosition().getRow())).setNotes(cssEditorFld.getText());
System.out.println("Hola mundo 627 "+" "+row +" "+col+" "+cssEditorFld.getText()+" "+((Contact) t.getTableView().getItems().get(t.getTablePosition().getRow())).notes);
}
});
System.out.println("Hola mundo 631 "+" "+row +" "+col+" "+val+" "+p+" "+cntac_name.getCellData(row)+" "+cssEditorFld.getText());
buttonNo.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
System.out.println(" Clicked No");
n = false;
s.hide();
}
});
}
});
I would not use the TableView editing API if you intend to edit the value externally to the table (it's not really clear how you would do this, or what the TextFieldTableCell you use is for if you intend to edit the data elsewhere).
Just use a cell factory that opens the new window to perform the editing when this cell is double-clicked. You can pass a callback (represented, for example, as a Consumer) to the window for updating the value in the table model.
Here is a SSCCE. The "Item" column edits in the "usual" way using the built-in TextFieldTableCell: the "Description" column edits using an external window.
import java.util.function.Consumer;
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
public class ExternalEditingTable extends Application {
private void showEditingWindow(Window owner, String currentValue, Consumer<String> commitHandler) {
Stage stage = new Stage();
stage.initOwner(owner);
stage.initModality(Modality.APPLICATION_MODAL);
TextArea textArea = new TextArea(currentValue);
Button okButton = new Button("OK");
okButton.setDefaultButton(true);
okButton.setOnAction(e -> {
commitHandler.accept(textArea.getText());
stage.hide();
});
Button cancelButton = new Button("Cancel");
cancelButton.setCancelButton(true);
cancelButton.setOnAction(e -> stage.hide());
HBox buttons = new HBox(5, okButton, cancelButton);
buttons.setAlignment(Pos.CENTER);
buttons.setPadding(new Insets(5));
BorderPane root = new BorderPane(textArea, null, null, buttons, null);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
table.setEditable(true);
TableColumn<Item, String> nameCol = column("Item", Item::nameProperty);
// just use standard editing approach as data is "small":
nameCol.setCellFactory(TextFieldTableCell.forTableColumn());
TableColumn<Item, String> descriptionCol = column("Description", Item::descriptionProperty);
// set up this column to show an editing window when double-clicked:
descriptionCol.setCellFactory(tc -> {
TableCell<Item, String> cell = new TableCell<Item, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : item);
}
};
cell.setOnMouseClicked(e -> {
if (e.getClickCount() == 2 && ! cell.isEmpty()) {
showEditingWindow(table.getScene().getWindow(), cell.getItem(), newValue -> {
Item item = table.getItems().get(cell.getIndex());
item.setDescription(newValue);
});
}
});
return cell ;
});
table.getColumns().add(nameCol);
table.getColumns().add(descriptionCol);
for (int i = 1 ; i <= 20 ; i++) {
table.getItems().add(new Item("Item "+i, "This is a long and\nwordy description of item "+i));
}
Scene scene = new Scene(table, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private static <S,T> TableColumn<S,T> column(String title, Function<S, Property<T>> prop) {
TableColumn<S,T> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> prop.apply(cellData.getValue()));
return col ;
}
public static class Item {
private final StringProperty description = new SimpleStringProperty() ;
private final StringProperty name = new SimpleStringProperty() ;
public Item(String name, String description) {
setName(name);
setDescription(description);
}
public final StringProperty descriptionProperty() {
return this.description;
}
public final String getDescription() {
return this.descriptionProperty().get();
}
public final void setDescription(final String description) {
this.descriptionProperty().set(description);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
}
public static void main(String[] args) {
launch(args);
}
}
Setting the cellFactory in a listener to the selection model is a bad practice. Rather than doing it this way, create a custom TableCell in the cellFactory for the column customize the editing by overwriting commitEdit, cancelEdit and startEdit.
Example:
TableView<StringProperty> tv = new TableView(FXCollections.observableArrayList(
new SimpleStringProperty("ajds bnas röu bngöspui rgöin röeisnpnhn reishn reinnb ökb rhkbhkbn afeae"),
new SimpleStringProperty("aap nja öaw njeaj nanja nja njja n"))
);
TableColumn<StringProperty, String> col = new TableColumn<>();
tv.getColumns().add(col);
tv.setEditable(true);
col.setCellValueFactory(cd -> cd.getValue());
col.setCellFactory(column -> new TableCell<StringProperty, String>() {
private final Text text;
private Stage editingStage;
{
text = new Text();
text.wrappingWidthProperty().bind(column.widthProperty());
setGraphic(text);
}
#Override
public void cancelEdit() {
super.cancelEdit();
closeStage();
}
private void closeStage() {
if (editingStage != null && editingStage.isShowing()) {
editingStage.setOnHidden(null);
editingStage.hide();
editingStage = null;
}
}
#Override
public void commitEdit(String newValue) {
super.commitEdit(newValue);
closeStage();
}
#Override
public void startEdit() {
super.startEdit();
// create editing ui
Button cancel = new Button("Cancel");
cancel.setCancelButton(true);
cancel.setOnAction(evt -> cancelEdit());
TextArea editor = new TextArea(getItem());
Button commit = new Button("OK");
commit.setOnAction(evt -> commitEdit(editor.getText()));
VBox vbox = new VBox(10, editor, commit, cancel);
// display editing window
Scene scene = new Scene(vbox);
editingStage = new Stage();
editingStage.initModality(Modality.WINDOW_MODAL);
editingStage.initOwner(getScene().getWindow());
editingStage.setScene(scene);
editingStage.setOnHidden(evt -> this.cancelEdit());
editingStage.show();
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
cancelEdit();
text.setText(item);
}
});

javafx : get the name of the column

How do I get the name of the column of a textfield inside a javaFX table?
I need this to check the value of the cells only in the "text2" column. I tried it with textfield.parent() but I didn't get a useful result.Edit: I just removed some unnessary log, which was not helpful for understanding.Now it is more convenient.
Here is my Code:
import java.util.ArrayList;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextArea;
import javafx.util.Callback;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
/*interface inside_table
{
public String get_column_name
}*/
public class Supermain extends Application {
#Override
public void start(Stage primaryStage) {
ArrayList myindizes=new ArrayList();
final TableView<myTextRow> table = new TableView<>();
table.setEditable(true);
table.setStyle("-fx-text-wrap: true;");
//Table columns
TableColumn<myTextRow, String> clmID = new TableColumn<>("ID");
clmID.setMinWidth(160);
clmID.setCellValueFactory(new PropertyValueFactory<>("ID"));
TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));
clmtext.setCellFactory(new TextFieldCellFactory());
TableColumn<myTextRow, String> clmtext2 = new TableColumn<>("Text2");
clmtext2.setMinWidth(160);
clmtext2.setCellValueFactory(new PropertyValueFactory<>("text2"));
clmtext2.setCellFactory(new TextFieldCellFactory());
//Add data
final ObservableList<myTextRow> data = FXCollections.observableArrayList(
new myTextRow(5, "Lorem","bla"),
new myTextRow(2, "Ipsum","bla")
);
table.getColumns().addAll(clmID, clmtext,clmtext2);
table.setItems(data);
HBox hBox = new HBox();
hBox.setSpacing(5.0);
hBox.setPadding(new Insets(5, 5, 5, 5));
Button btn = new Button();
btn.setText("Get Data");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
for (myTextRow data1 : data) {
System.out.println("data:" + data1.getText2());
}
}
});
hBox.getChildren().add(btn);
BorderPane pane = new BorderPane();
pane.setTop(hBox);
pane.setCenter(table);
primaryStage.setScene(new Scene(pane, 640, 480));
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public static class TextFieldCellFactory
implements Callback<TableColumn<myTextRow, String>, TableCell<myTextRow, String>> {
#Override
public TableCell<myTextRow, String> call(TableColumn<myTextRow, String> param) {
TextFieldCell textFieldCell = new TextFieldCell();
return textFieldCell;
}
public static class TextFieldCell extends TableCell<myTextRow, String> {
private TextArea textField;
private StringProperty boundToCurrently = null;
private String last_text;
public TextFieldCell() {
textField = new TextArea();
textField.setWrapText(true);
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
last_text="";
this.setGraphic(textField);
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
//only if textfield is in the text2 column
if(isNowFocused){last_text=textField.getText(); System.out.println("NOW focus "+last_text);}
if (! isNowFocused && ! isValid(textField.getText())) {
textField.setText(last_text);
textField.selectAll();
System.out.println("blur");
}
});
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
// myindizes.add(getIndex());
// Retrieve the actual String Property that should be bound to the TextField
// If the TextField is currently bound to a different StringProperty
// Unbind the old property and rebind to the new one
ObservableValue<String> ov = getTableColumn().getCellObservableValue(getIndex());
SimpleStringProperty sp = (SimpleStringProperty) ov;
if (this.boundToCurrently == null) {
this.boundToCurrently = sp;
this.textField.textProperty().bindBidirectional(sp);
} else if (this.boundToCurrently != sp) {
this.textField.textProperty().unbindBidirectional(this.boundToCurrently);
this.boundToCurrently = sp;
this.textField.textProperty().bindBidirectional(this.boundToCurrently);
}
double height = real_lines_height(textField.getText(), this.getWidth(), 30, 22);
textField.setPrefHeight(height);
textField.setMaxHeight(height);
textField.setMaxHeight(Double.MAX_VALUE);
// if height bigger than the biggest height in the row
//-> change all heights of the row(textfields ()typeof textarea) to this height
// else leave the height as it is
//System.out.println("item=" + item + " ObservableValue<String>=" + ov.getValue());
//this.textField.setText(item); // No longer need this!!!
} else {
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}//update
private boolean isValid(String s){
if(s.length()<7){return true;}
return false;
}
}
}
public class myTextRow {
private final SimpleIntegerProperty ID;
private final SimpleStringProperty text;
private final SimpleStringProperty text2;
public myTextRow(int ID, String text,String text2) {
this.ID = new SimpleIntegerProperty(ID);
this.text = new SimpleStringProperty(text);
this.text2 = new SimpleStringProperty(text2);
}
//setter
public void setID(int id) {
this.ID.set(id);
}
public void setText(String text) {
this.text.set(text);
}
public void setText2(String text) {
this.text2.set(text);
}
//getter
public int getID() {
return ID.get();
}
public String getText() {
return text.get();
}
public String getText2() {
return text2.get();
}
//properties
public StringProperty textProperty() {
return text;
}
public StringProperty text2Property() {
return text2;
}
public IntegerProperty IDProperty() {
return ID;
}
}
private static double real_lines_height(String s, double width, double heightCorrector, double widthCorrector) {
HBox h = new HBox();
Label l = new Label("Text");
h.getChildren().add(l);
Scene sc = new Scene(h);
l.applyCss();
double line_height = l.prefHeight(-1);
int new_lines = s.replaceAll("[^\r\n|\r|\n]", "").length();
// System.out.println("new lines= "+new_lines);
String[] lines = s.split("\r\n|\r|\n");
// System.out.println("line count func= "+ lines.length);
int count = 0;
//double rest=0;
for (int i = 0; i < lines.length; i++) {
double text_width = get_text_width(lines[i]);
double plus_lines = Math.ceil(text_width / (width - widthCorrector));
if (plus_lines > 1) {
count += plus_lines;
//rest+= (text_width / (width-widthCorrector)) - plus_lines;
} else {
count += 1;
}
}
//count+=(int) Math.ceil(rest);
count += new_lines - lines.length;
return count * line_height + heightCorrector;
}
private static double get_text_width(String s) {
HBox h = new HBox();
Label l = new Label(s);
l.setWrapText(false);
h.getChildren().add(l);
Scene sc = new Scene(h);
l.applyCss();
return l.prefWidth(-1);
}
}
There are probably (way) better ways to organize this, but probably the cleanest fix is just to define a boolean validate parameter to the constructor of your cell implementation. (You really don't want the logic to be "if the title of the column is equal to some specific text, then validate". You would be utterly screwed when your boss came in to the office and asked you to internationalize the application, or even just change the title of the column, for example.)
Using an entire inner class just to implement the callback seems completely redundant, but keeping that you would have to pass the parameter through it:
public static class TextFieldCellFactory
implements Callback<TableColumn<myTextRow, String>, TableCell<myTextRow, String>> {
private final boolean validate ;
public TextFieldCellFactory(boolean validate) {
this.validate = validate ;
}
#Override
public TableCell<myTextRow, String> call(TableColumn<myTextRow, String> param) {
TextFieldCell textFieldCell = new TextFieldCell(validate);
return textFieldCell;
}
public static class TextFieldCell extends TableCell<myTextRow, String> {
private TextArea textField;
private StringProperty boundToCurrently = null;
private String last_text;
public TextFieldCell(boolean validate) {
textField = new TextArea();
textField.setWrapText(true);
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
last_text="";
this.setGraphic(textField);
if (validate) {
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
//only if textfield is in the text2 column
if(isNowFocused){last_text=textField.getText(); System.out.println("NOW focus "+last_text);}
if (! isNowFocused && ! isValid(textField.getText())) {
textField.setText(last_text);
textField.selectAll();
System.out.println("blur");
}
});
}
}
// ...
}
Then of course you just do
TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));
clmtext.setCellFactory(new TextFieldCellFactory(false));
TableColumn<myTextRow, String> clmtext2 = new TableColumn<>("Text2");
clmtext2.setMinWidth(160);
clmtext2.setCellValueFactory(new PropertyValueFactory<>("text2"));
clmtext2.setCellFactory(new TextFieldCellFactory(true));
(To properly answer your question, you can get the text of the column from within the cell to which it is attached with getTableColumn().getText(), but as I pointed out, actually basing the logic on the value displayed in a column header will make your code completely unmaintainable.)
And I guess for completeness, I should also mention that your TextFieldCellFactory class looks like it is not really serving any purpose. I would remove it entirely and just have the TextFieldCell class, and do
TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));
clmtext.setCellFactory(c -> new TextFieldCell(false));
TableColumn<myTextRow, String> clmtext2 = new TableColumn<>("Text2");
clmtext2.setMinWidth(160);
clmtext2.setCellValueFactory(new PropertyValueFactory<>("text2"));
clmtext2.setCellFactory(c -> new TextFieldCell(true));

Changing table row color using a property that would not be visible in any column

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:
create a model class Person (serialNumber, first, last).
create an observableList of Person using an extractor.
create two tableviews(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:
the style of 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.
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 stylePropertyof the row to its items serialNumberPropertylike 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".
Link to full example.

filling TreeTableView in fxml dynamicly from JavaFX

I want to fill a TreeTableView dynamically. I have created the view with Scenebuilder. In MyController I have a Draw button which by cliking calls a method, which makes a treetable. I have references set. when i click the button only columns are appear. The first column which is ought to show the treecolumn shows nothing and there is also no other value under columns. The TreeTableView works when I just use it alone and add it to scene, but now i want to use the values to fill the treeTable which is made by scene Builder. There is something else that i can not figure out. The columns are set correctly when comment out treeTable = new TreeTableView<>(root); which is under treeTable comment! otherwise the columns are not shown either.
That is what i want to see:
package controller;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import model.DataConstructor;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.TreeTableColumn.CellDataFeatures;
import javafx.util.Callback;
public class MainController implements Initializable {
private TreeItem<String> root = new TreeItem<>("Functions");
private DataConstructor dc = new DataConstructor();
#FXML
private TreeTableView<String> treeTable;
#Override
public void initialize(URL location, ResourceBundle resources) {
// TODO Auto-generated method stub
}
public void testDraw(ActionEvent event) {
drawTable();
}
private void drawTable() {
root.setExpanded(true);
Set<String> combinedKeys = new HashSet<>(dc.getCombiFunc().keySet());
Set<String> funcAllKeys = new HashSet<>(dc.getSortedfuncAll().keySet());
funcAllKeys.removeAll(dc.getCombiFunc().keySet());
for (List<String> value : dc.getCombiFunc().values()) {
funcAllKeys.removeAll(value);
}
for (String valueremained : funcAllKeys) {
ArrayList<String> tempNameId = new ArrayList<>();
tempNameId.add(dc.getSortedfuncAll().get(valueremained));
// all elements which are not in combined functions (They are all
// orphan)
root.getChildren().add(new TreeItem<String>(tempNameId.get(0)));
}
Set<String> keyFromcombined = new HashSet<>();
List<String> valueOfCombined = new ArrayList<String>();
for (Entry<String, List<String>> ent : dc.getCombiFunc().entrySet()) {
valueOfCombined.add(ent.getValue().get(0));
}
List<String> rootKeyList = new ArrayList<>();
for (String key : combinedKeys) {
if (!valueOfCombined.contains((key))) {
keyFromcombined.add(dc.getFuncAll().get(key));
rootKeyList.add(key);
}
}
String[] rootKeys = rootKeyList.toArray(new String[rootKeyList.size()]);
// ////////////////treetable////////////////////////////
treeTable = new TreeTableView<>(root);
Arrays.stream(rootKeys).forEach(
rootKey -> root.getChildren().add(
createTreeItem(dc.getCombiFunc(), rootKey)));
// ////////////////First column/////////////////////////
TreeTableColumn<String, String> firstColumn = new TreeTableColumn<>("");
treeTable.getColumns().add(firstColumn);// Tree column
firstColumn
.setCellValueFactory(new Callback<CellDataFeatures<String, String>, ObservableValue<String>>() {
public ObservableValue<String> call(
CellDataFeatures<String, String> p) {
return new ReadOnlyStringWrapper(p.getValue()
.getValue());
}
});
// //////////////////Rest Columns////////////////////////
for (Entry<String, String> ent : dc.getSortedAssignedOrg().entrySet()) {
TreeTableColumn<String, ArrayList<String>> col = new TreeTableColumn<>();
Label label = new Label(ent.getValue());
col.setGraphic(label);
label.setTooltip(new Tooltip(label.getText()));// tooltip for column
// headers
col.setPrefWidth(45);
//cell Value Factory////////////////////////
col.setCellValueFactory(new Callback<TreeTableColumn.CellDataFeatures<String, ArrayList<String>>, ObservableValue<ArrayList<String>>>() {
#Override
public ObservableValue<ArrayList<String>> call(
CellDataFeatures<String, ArrayList<String>> param) {
TreeMap<String, List<String>> temp = (TreeMap<String, List<String>>) dc
.getFuncTypeOrg().clone();
ArrayList<String> result = new ArrayList<>();
for (int i = 0; i < dc.getFuncTypeOrg().size(); i++) {
List<String> list = temp.firstEntry().getValue();
String key = temp.firstEntry().getKey();
// root.getChildren();
if (list.get(1).equals(param.getValue().getValue())
&& list.get(5).equals(label.getText())) {
result.add(0, list.get(2));// weight
// //////////////org combi TODO
for (Entry<String, Set<String>> ent : dc
.getCombiOrg().entrySet()) {
if (ent.getKey().contains(col.getText()))
for (Set<String> value : dc.getCombiOrg()
.values()) {
if (value.contains(col.getText()))
System.out.println(col.getText());
}
}
// ///////////////org combi
if (list.size() > 6) {
result.add(1, list.get(list.size() - 1));// color
result.add(2, list.get(6));// App component
}
else
result.add("white");
result.add("noOrg");
} else {
temp.remove(key);
}
}
return new ReadOnlyObjectWrapper<ArrayList<String>>(result);
}
});
// //////////////cellfactory/////////////////////////
col.setCellFactory(new Callback<TreeTableColumn<String, ArrayList<String>>, TreeTableCell<String, ArrayList<String>>>() {
#Override
public TreeTableCell<String, ArrayList<String>> call(
TreeTableColumn<String, ArrayList<String>> param) {
return new TreeTableCell<String, ArrayList<String>>() {
public void updateItem(ArrayList<String> item,
boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setStyle("");
setText("");
} else if (item.contains("Green")) {
float weightInt = Float.parseFloat(item.get(0));
float res = weightInt * 1;
String resString = Float.toString(res);
this.setStyle("-fx-background-color:green");
setTooltip(new Tooltip(item.get(2)));
setText(resString);
} else if (item.contains("yellow")) {
this.setStyle("-fx-background-color:yellow");
setTooltip(new Tooltip(item.get(2)));
setText("0");
} else if (item.contains("white")) {
setText("DD");
}
}
};
};
});
treeTable.getColumns().add(col);
}
// end for col
treeTable.setPrefWidth(1200);
treeTable.setPrefHeight(500);
treeTable.setShowRoot(false);
treeTable.setTableMenuButtonVisible(true);
}
/**
* Create a TreeItem for a TreeView from a set of data given the data and an
* identified root within the data.
*/
private TreeItem<String> createTreeItem(TreeMap<String, List<String>> data,
String rootKey) {
TreeItem<String> item = new TreeItem<>();
item.setValue(rootKey);
item.setExpanded(true);
List<String> childData = data.get(rootKey);
if (childData != null) {
childData.stream().map(child -> createTreeItem(data, child))
.collect(Collectors.toCollection(item::getChildren));
}
String valueName = item.getValue();
//String sorteV = dc.getSortedfuncAll().get(valueName);
item.setValue((dc.getSortedfuncAll().get(valueName)));
return item;
}
}
Your drawTable() method creates a new TreeTableView but I don't see any code where you add that TreeTableView to the UI, or remove the existing TreeTableView.
You probably want to replace
treeTable = new TreeTableView<>(root);
with
treeTable.setRoot(root);

Resources