I have a tableView with text message change color in term of a type message.
I have the following code :
tableViewErreur.setRowFactory(param -> new TableRow<BoErreur>() {
#Override
protected void updateItem(BoErreur paramT, boolean empty) {
super.updateItem(paramT, empty);
if (!isEmpty() && paramT != null) {
switch (paramT.getNiveauErreur()) {
case 0:
setId(ConstantsUI.CSS_ERREUR_INFO);
break;
case 1:
setId(ConstantsUI.CSS_ERREUR);
break;
default:
break;
}
tableViewErreur.refresh();
}
}
});
And I have the following code to have a wrap text in my cell :
tableColumnErreur.setCellFactory(new Callback<TableColumn<BoErreur, String>, TableCell<BoErreur, String>>() {
#Override
public TableCell<BoErreur, String> call(TableColumn<BoErreur, String> arg0) {
return new TableCell<BoErreur, String>() {
private Text text;
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!isEmpty()) {
text = new Text(item.toString());
text.setWrappingWidth(tableColumnErreur.getWidth());
this.setWrapText(true);
setGraphic(text);
}
}
};
}
});
The problem is that my second code remove the cell's color. And I need to make :
switch (paramT.getNiveauErreur())
in the RowFactory to determinate my row's color.
Help please,
Thanks.
If you want to make a different colors in odd and even rows you need only to use this code in your .css file.
.table-row-cell:odd{
//add color here
}
.table-row-cell:even{
//add color here
}
Related
For some reason when I want to make a row bold it's not working. Same if I try setting the style on the children. I think this was working in the past. I'm on the latest 11.0.2 version.
see below for my testing code. I can use color though. Color works, font doesn't.
Thanks!
emailsTableView.setRowFactory(new Callback<TableView<EmailMessage>, TableRow<EmailMessage>>() {
#Override
public TableRow<EmailMessage> call(TableView<EmailMessage> param) {
return new TableRow<>() {
#Override
protected void updateItem(EmailMessage item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
if (item.isRead()) {
setStyle("");
} else {
//setStyle("-fx-font-weight: bold");
//setStyle("fx-font-style: italic");
//setStyle("-fx-background-color: purple");
for (int i=0; i < getChildren().size(); i++) {
getChildren().get(i).setStyle("-fx-font-weight: bold");
//getChildren().get(i).setStyle("-fx-background-color: purple");
}
}
System.out.println("setupBoldRows " + item + " style: " + getStyle());
}
}
};
}
}
I cannot tell you the exact cause for your issue (as i am not working on JavaFX 11 environment). But I would like you to give a try with css styleSheet to see if that is working.
In your css file :
.table-row-cell:unread{
-fx-font-weight:bold;
}
In Code:
// Create a unread pseudo state for row.
final PseudoClass unreadPseudoState = PseudoClass.getPseudoClass("unread");
emailsTableView.setRowFactory(new Callback<TableView<EmailMessage>, TableRow<EmailMessage>>() {
#Override
public TableRow<EmailMessage> call(TableView<EmailMessage> param) {
return new TableRow<>() {
#Override
protected void updateItem(EmailMessage item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
// Update the pseudo state of the row to pick the styling from styleSheet
this.pseudoClassStateChanged(unreadPseudoState, !item.isRead());
}
}
};
}
});
I have tried searching both google and stackoverflow for answer to my question but I could not find any.
I have a program that adds/reads data from a database and to a tableview. I want to be able to edit the previously entered data from the tableview by adding two buttons (Save and abort) to the current row being edited.
I am having some troubles understanding the routines like Callback etc. But I have managed to get a column with two buttons to appear when I start the edit. But I get buttons on every column I just want the buttons on the currently
selected row. Also I don't really understand how to get the current object from the save-button to be able to save it.
And also how the abort-button should work to cancel all changes.
public void setUpTableView() {
columnAnkomstdatum.setCellValueFactory(new PropertyValueFactory<>("arrivalDate"));
columnSupplier.setCellValueFactory(new PropertyValueFactory<>("supplier"));
columnRadiopharmaceutical.setCellValueFactory(new PropertyValueFactory<>("radiopharmaceutical"));
columnActivity.setCellValueFactory(new PropertyValueFactory<>("startActivity"));
columnCalibrationdate.setCellValueFactory(new PropertyValueFactory<>("startDate"));
columnBatchNumber.setCellValueFactory(new PropertyValueFactory<>("batchNumber"));
columnContaminationControl.setCellValueFactory(new PropertyValueFactory<>("contaminationControll"));
columnRoom.setCellValueFactory(new PropertyValueFactory<>("room"));
columnUser.setCellValueFactory(new PropertyValueFactory<>("user"));
tableview.setEditable(true);
columnSupplier.setEditable(true);
columnSupplier.setCellFactory(ComboBoxTableCell.forTableColumn(supplierList));
columnSupplier.setOnEditCommit(t -> {
ArrayList<Radiopharmaceutical> radioListfromSupplier = new RadiopharmaceuticalDao().getRadiopharmaceuticalsBySupplierName(t.getNewValue().getSupplierName());
radioList = FXCollections.observableArrayList(radioListfromSupplier);
t.getRowValue().setSupplier(t.getNewValue());
columnRadiopharmaceutical.setCellFactory(ComboBoxTableCell.forTableColumn(radioList));
if(tableview.getColumns().size() <= 9) {
addButtonsToTable();
}
});
}
private void addButtonsToTable() {
TableColumn<RegRadio, Void> editRow = new TableColumn<>("Edit");
tableview.getColumns().add(editRow);
Callback<TableColumn<RegRadio, Void>, TableCell<RegRadio, Void>> cellFactory = new Callback<TableColumn<RegRadio,Void>, TableCell<RegRadio,Void>>() {
#Override
public TableCell<RegRadio, Void> call(final TableColumn<RegRadio, Void> param) {
final TableCell<RegRadio, Void> cell = new TableCell<RegRadio, Void>() {
private final Button btnSave = new Button("Save");
private final Button btnAbort = new Button("Avbryt");
{
btnSave.setOnAction((ActionEvent event) -> {
RegRadio rr = getTableView().getItems().get(getIndex());
System.out.println("Saved");
});
}
{
btnAbort.setOnAction((ActionEvent event) -> {
System.out.println("Abort");
});
}
#Override
public void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
HBox pane = new HBox(btnSave, btnAbort);
setGraphic(pane);
}
}
};
return cell;
}
};
editRow.setCellFactory(cellFactory);
tableview.getColumns().add(editRow);
}
In your updateItem callback you can check if the cell is in the selected row in order to decide if you should show the buttons or not. Additionally you also need a flag to check if the user is editing. Something like this:
#Override
public void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
var selectedCells = tableview.getSelectionModel().getSelectedCells();
if (empty || !isEditing || selectedCells.isEmpty || getTableRow().getIndex() != selectedCells.get(0).getRow()) {
setGraphic(null);
} else {
HBox pane = new HBox(btnSave, btnAbort);
setGraphic(pane);
}
}
Another approach would be to change the visibility of the buttons depending on whether the cell is in the selected row:
#Override
public void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
HBox pane = new HBox(btnSave, btnAbort);
var selectedCells = tableview.getSelectionModel().getSelectedCells();
pane.setVisible(!selectedCells.isEmpty() && getTableRow().getIndex() == selectedCells.get(0).getRow())
setGraphic(pane);
}
}
(I haven't compiled and tested these code samples)
I made a treeview with normal treecells and a cellfactory to display my custom objects.
I wanned to be able to edit the object names in the treeview.
So i switched from a normal TreeCell to a TextFieldTreeCell so that i can edit the name of my objects in the tree.
But now the setGrapic methods in updateitem is not working. what am i doing wrong?
Also im not sure how to acces the custom object from the fromString methode for editing the object.
treeView.setCellFactory(new Callback<TreeView<MenuItem>, TreeCell<MenuItem>>() {
#Override
public TreeCell<MenuItem> call(TreeView<MenuItem> menuItemTreeView) {
// MenuItemTreeCell menuItemTreeCell = new MenuItemTreeCell();
TextFieldTreeCell<MenuItem> cell = new TextFieldTreeCell<MenuItem>(new StringConverter<MenuItem>() {
#Override
public String toString(MenuItem menuItem) {
if(menuItem==null){
return "";
}
return menuItem.getName();
}
#Override
public MenuItem fromString(String s) {
// todo add code for changing name
return null;
}
})
{
#Override
public void updateItem(MenuItem item, boolean empty) { // empty was b
super.updateItem(item, empty);
// setText((empty || item == null) ? "" : item.getName());
// checking if not null , and then what class to determ icon
if (item != null) {
switch (item.getClass().getSimpleName()) {
case "Group":
setGraphic(new ImageView(caseicon));
break;
case "Item":
setGraphic(new ImageView(basicicon));
break;
case "ClassItem":
setGraphic(new ImageView(classicon));
break;
case "MechanicItem":
setGraphic(new ImageView(mechanicicon));
break;
}
} else {
// no icon
setGraphic(null);
}
super.updateItem(item, empty);
}
};
I have the following problem:
I have ListView<ViewsRecord> where ViewsRecord has property int favorites.
If favorites > 0 it's in favorites, and if favorites == 0 it's a regular row (not favorite).
Now what I want to do is:
When user selects cells (in multiple select mode) those cells will have default selected background (like: -fx-background-color: -fx-selection-bar)
When cells aren't selected by the user:
2.1. if cell isn't in favorites it has regular bacground (ex. white)
2.2. if cell is in favorites it has green background
So far I came up with this solution, but it's ugly code, and I wonder if there's easiest way to do that.
Further more, I had to use setUserData() to check if cell should be selected or not, otherwise during the list scroll, or select - cells had randomly changed their colors. (I assume it's because reusable objects are stored in memory and updateItem() isn't always fired).
Here's my code:
list.setCellFactory(new Callback<ListView<ViewsRecord>, ListCell<ViewsRecord>>(){
#Override
public ListCell<ViewsRecord> call(ListView<ViewsRecord> param) {
ListCell<ViewsRecord> cell = new ListCell<ViewsRecord>(){
#Override
protected void updateItem(ViewsRecord item, boolean empty) {
super.updateItem(item, empty);
if(!empty){
if(item.getFavorites() > 0){ //favorite view
if(!isSelected()){
setStyle("-fx-background-color: darkseagreen;");
}
setUserData(new Integer(1));
} else { //normal view
if(!isSelected()){
setStyle("-fx-background-color: white;");
}
setUserData(new Integer(0));
}
setText(item.toString());
} else { //empty view
setText(null);
setStyle("-fx-background-color: white;");
setUserData(new Integer(0));
}
}
};
//fix bacground color when cell is selected
cell.selectedProperty().addListener( (obsVal, oldVal, newVal) -> {
if(newVal){
cell.setStyle("-fx-background-color: -fx-selection-bar;");
} else {
if((Integer)cell.getUserData() == 1){ //favorite
cell.setStyle("-fx-background-color: darkseagreen;");
} else { //normal
cell.setStyle("-fx-background-color: white;");
}
}
});
return cell;
}
});
EDIT
Thanks to jns I've managed to simplify the code.
Current version:
list.setCellFactory(new Callback<ListView<ViewsRecord>, ListCell<ViewsRecord>>(){
#Override
public ListCell<ViewsRecord> call(ListView<ViewsRecord> param) {
final PseudoClass FAVORITE_PSEUDO_CLASS = PseudoClass.getPseudoClass("favorite");
ListCell<ViewsRecord> cell = new ListCell<ViewsRecord>(){
#Override
protected void updateItem(ViewsRecord item, boolean empty) {
if(!empty){
//favorite or not, and not selected
pseudoClassStateChanged(FAVORITE_PSEUDO_CLASS, (item.getFavorites() > 0) && !isSelected());
setText(item.toString());
} else {
setText(null);
//empty
pseudoClassStateChanged(FAVORITE_PSEUDO_CLASS, false);
}
super.updateItem(item, empty);
}
};
cell.selectedProperty().addListener( (obsVal, oldVal, newVal) -> {
if(newVal){
//selected
cell.pseudoClassStateChanged(FAVORITE_PSEUDO_CLASS, false);
} else {
//favorite or not
cell.pseudoClassStateChanged(FAVORITE_PSEUDO_CLASS, cell.getItem().getFavorites() > 0);
}
});
return cell;
}
});
You could use a PseudoClass for styling the listcell according to the favorite property.
public class YourListCell extends ListCell<ViewsRecord> {
private static PseudoClass FAVORITE_PSEUDO_CLASS = PseudoClass.getPseudoClass("favorite");
#Override
protected void updateItem(ViewsRecord item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
boolean isFavorite = item > 0;
pseudoClassStateChanged(FAVOURITE_PSEUDO_CLASS, isFavorite);
...
}
}
This allows you to style your listcell via css in your stylesheet:
.list-cell:favorite {
-fx-background-color: darkseagreen;
}
ListCell contains a getItem() method. Thus you can get access to your data object. Furthermore you can use its PseudoClass empty and selected to transfer more of the style handling to the stylesheet:
.list-cell, .list-cell:empty {
-fx-background-color: white
}
.list-cell:favorite{
-fx-background-color: darkseagreen;
}
.list-cell:selected {
-fx-background-color: -fx-selection-bar;
}
I have a table view and inside it, there is one column filled with TextField.
There is no problem when I have few data and my table do not have scroll bar, all TextFields appears.
The problem is, when I scroll down my table and then goes up again, some TextFields are missing.
Here is my code for the column filled with TextField:
purchaseQtyCol.setCellFactory(
new Callback<TableColumn< TransactionModel, TextField>, TableCell< TransactionModel, TextField>>() {
#Override
public TableCell< TransactionModel, TextField> call(final TableColumn< TransactionModel, TextField> p) {
TableCell<TransactionModel, TextField> cell = new TableCell<TransactionModel, TextField>() {
#Override
public void updateItem(TextField item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
/**
* for(CheckBox cbb : canceledCB) {
* if(item.equals(cbb))
* System.out.println("aa" +
* this.indexProperty().getValue() + " " +
* item.isSelected() ); }*
*/
this.setGraphic(item);
}
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
purchaseQtyCol.setCellValueFactory(new Callback<CellDataFeatures<TransactionModel, TextField>, ObservableValue<TextField>>() {
#Override
public ObservableValue<TextField> call(final CellDataFeatures<TransactionModel, TextField> p) {
System.out.println("new textfield");
final TextField qtyField = new TextField() {
#Override
public void replaceText(int start, int end, String text) {
if (text.matches("[0-9]") || text.equals("")) {
super.replaceText(start, end, text);
if (this.getText().isEmpty()) {
p.getValue().setPurchaseQty(0);
p.getValue().setTotalPrice(0);
} else {
p.getValue().setPurchaseQty(Integer.parseInt(this.getText()));
p.getValue().setTotalPrice(p.getValue().purchaseQtyProperty().intValue() * p.getValue().basePriceProperty().intValue());
}
recountTotals();
}
}
#Override
public void replaceSelection(String text) {
if (text.matches("[0-9]") || text.equals("")) {
super.replaceSelection(text);
if (this.getText().isEmpty()) {
p.getValue().setPurchaseQty(0);
p.getValue().setTotalPrice(0);
} else {
p.getValue().setPurchaseQty(Integer.parseInt(this.getText()));
p.getValue().setTotalPrice(p.getValue().purchaseQtyProperty().intValue() * p.getValue().basePriceProperty().intValue());
}
recountTotals();
}
}
};
qtyField.setText("" + p.getValue().purchaseQtyProperty().getValue());
qtyField.setAlignment(Pos.CENTER_RIGHT);
return new SimpleObjectProperty(qtyField);
}
});
I really appreciate helps from you guys.
Regards,
Chrisma Andhika
Chrisma! The problem you face is a famous issue for JavaFX about updating items in tableview. There is already some workaround about it, for example here. The solution was to trigger tableview's internal update mechanism by
tableview.getColumns().get(0).setVisible(false);
tableview.getColumns().get(0).setVisible(true);
But as far as I could understand this solution affected only changes of data, not the style of tableview's nodes.
I also used
#Override
public void updateItem(Object item, boolean empty)
with my implementation, but it was not enough, because when there were too many rows in a table and scroll bar appeared the updating of rows became an absolute mess.
What I needed is to make highlighted all visible rows satifying some criteria by using css. I achieved that in the following way:
Callback<TableView<Person>, TableRow<Person>> callBack =
new Callback<TableView<Person>, TableRow<Person>>() {
#Override
public TableRow<Person> call(TableView<Person> tableView) {
final TableRow<Person> row = new TableRow<Person>() {
#Override
public void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if(!empty && item.getFirstName() == "John") {
getStyleClass().add("john");
}
}
};
row.visibleProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue observableValue, Object t, Object t1) {
Boolean oldValue = (Boolean)t;
Boolean newValue = (Boolean)t1;
if(oldValue && !newValue) {
row.getStyleClass().remove("john");
}
if(!oldValue && newValue) {
if(row.getItem().getFirstName() == "John")
row.getStyleClass().add("john");
}
}
});
return row;
}
};
tableview.setRowFactory(callBack);
The css file should contain the following lines:
.john {
-fx-background-color: forestgreen;
-fx-text-fill: white;
-fx-border-style: solid;
}
Of course you may choose different styling of rows.
The Person class should contain
public SimpleStringProperty firstNameProperty() {
return firstName;
}
As you can see I added the listener to the VisibleProperty of a row, where I control the css behavior of each row. Maybe this idea could help to solve data update in tableview in some cases, not just rows styling.
I should aslo add that I started receiving the NullPointerException in the public void changed method in the code above, although it doesn't affect the result of my programm.
I hope it will help you, Chrisma! And others as well!
I just had that problem with checkboxes. They randomly dissapeared when I scrolled my big table rapidly. I solved it just by deleting this line in my cellFactory:
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
// setGraphic(null); (This line causes weird behaviour in scrolling, delete it)
} else {
checkBox.setSelected(item);
}
}
Well, I also had the same problem, apparently only draw the visible text field, when we use the scroll does not appear, because only drawing the scene but not the textinputcontrol , so my solution is to capture the scroll event and when you use it to resize the textfield and return it to its original size, thus forcing you to repaint the object, now appear with textinputcontrol.forcing repaint the object, now appear with textinputcontrol.
tableview.addEventFilter(ScrollEvent.ANY, new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent scrollEvent) {
System.out.println("Scrolled.");
for(ObservableList<TextField> i:data)
{
for(TextField j: i)
{
j.setPrefSize(141, 31);
j.setPrefSize(140, 30);
}
}
}
});