Neither startEdit or setOnEditCommit getting called - javafx

I followed this example mentioned on this link -
UITableView - Better Editing through Binding?
I changed it a bit accordingly
Model class -
public static class TableData {
private String firstName, lastName;
private TableData(String first, String last) {
this.firstName = first;
this.lastName = last;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Custom Cell factory -
public static class TextFieldCellFactory implements Callback<TableColumn<TableData, String>, TableCell<TableData, String>> {
#Override
public TableCell<TableData, String> call(TableColumn<TableData, String> param) {
TextFieldCell textFieldCell = new TextFieldCell();
return textFieldCell;
}
public static class TextFieldCell extends TableCell<TableData, String> {
private TextField textField;
private StringProperty boundToCurrently = null;
private String newval = "";
public TextFieldCell() {
textField = new TextField();
textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
System.out.println("key pressed");
commitEdit(textField.getText());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
textField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
// commitEdit(newValue);
System.out.println("" + newValue);
newval = newValue;
}
});
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if(!newValue){
System.out.println("losing focus" + newval);
//commichange();
commitEdit(textField.getText());
}
}
});
this.setGraphic(textField);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.setText(item);
} else {
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
}
setting onEditCommit-
c1.setOnEditCommit(
new EventHandler<TableColumn.CellEditEvent<TableData, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<TableData, String> t) {
System.out.println("ON edit commit" + t);
((TableData) t.getTableView().getItems().get(
t.getTablePosition().getRow())).setFirstName(t.getNewValue());
}
}
);
Problem 1- I want to know what happens when commitEdit() is called. Does it invoke setOnEditCommit ? If it does then why its not invoking setOnEditCoommit
Problem 2 - Why its not going into setOnEditCommit?
Problem 3 I applied startEdit just to check if its enters that field. But that method also is not getting invoked.
Can anyone specify what i am missing here.I don't want a workaround. I need to understand whats the reason behind it
P.S I have removed the binding properties as given in the link.

Your table never enters an editing state (because you never ask it to). Because the cell never has isEditing() return true, the default commitEdit() method becomes a no-op.
You need the TableView to know that it has to start editing a cell when the text field in that cell receives focus. You can do this by modifying the focus listener on the text field:
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
getTableView().edit(getIndex(), getTableColumn());
} else {
commitEdit(textField.getText());
}
}
});

Related

JavaFX TableView Cell color change depending on text value

I have a JavaFX desktop app with a TableView. I populate the data using a POJO named Orders which ultimately comes from a Firebird SQL database.
Image of what I have now
What I am looking to do is change the background fill color of each cell in the first column 'Status' depending on the text value. So if the text value is 'READY' then green, 'STARTED' will be yellow and 'DONE' will be gray.
Image of what I would like
Here is the code portion I use to populate the TableView:
`
#FXML private TableView<Orders> tblOrders;
#FXML private TableColumn<Orders, Integer> clmStatus;
#FXML private TableColumn<Orders, String> clmStartDateTime;
#FXML private TableColumn<Orders, String> clmShopOrder;
#FXML private TableColumn<Orders, String> clmRotation;
#FXML private TableColumn<Orders, String> clmGMIECode;
#FXML private TableColumn<Orders, String> clmSAPCode;
#FXML private TableColumn<Orders, Integer> clmLineName;
#FXML private TableColumn<Orders, Integer> clmOrderProductionNr;
private ObservableList<Orders> list;
public void initialize(URL location, ResourceBundle resources) {
populateTable();
}
private void populateTable() {
log.appLog("Populating table\r\n");
clmStatus.setCellValueFactory(new PropertyValueFactory<>("status"));
clmStartDateTime.setCellValueFactory(new PropertyValueFactory<>
("startDateTime"));
clmShopOrder.setCellValueFactory(new PropertyValueFactory<>("extra1"));
clmRotation.setCellValueFactory(new
PropertyValueFactory<("batchLotNr"));
clmGMIECode.setCellValueFactory(new PropertyValueFactory<>("wareNr"));
clmSAPCode.setCellValueFactory(new PropertyValueFactory<>
("serviceDescription"));
clmLineName.setCellValueFactory(new PropertyValueFactory<>
("productionLineNr"));
clmOrderProductionNr.setCellValueFactory(new PropertyValueFactory<>
("orderProductionNr"));
tblOrders.setItems(list);
}
`
Code sample of my Orders POJO:
`
public class Orders {
private final SimpleStringProperty status;
private final SimpleStringProperty startDateTime;
private final SimpleStringProperty extra1;
private final SimpleStringProperty batchLotNr;
private final SimpleStringProperty wareNr;
private final SimpleStringProperty serviceDescription;
private final SimpleStringProperty productionLineNr;
private final SimpleIntegerProperty orderProductionNr;
Orders(String status, String startDateTime, String extra1, String batchLotNr, String wareNr, String serviceDescription, String productionLineNr, int orderProductionNr) {
this.status = new SimpleStringProperty(status);
this.startDateTime = new SimpleStringProperty(startDateTime);
this.extra1 = new SimpleStringProperty(extra1);
this.batchLotNr = new SimpleStringProperty(batchLotNr);
this.wareNr = new SimpleStringProperty(wareNr);
this.serviceDescription = new SimpleStringProperty(serviceDescription);
this.productionLineNr = new SimpleStringProperty(productionLineNr);
this.orderProductionNr = new SimpleIntegerProperty((orderProductionNr));
}
public String getStatus() {
return status.get();
}
public String getStartDateTime() {return startDateTime.get(); }
public String getExtra1() {
return extra1.get();
}
public String getBatchLotNr() {
return batchLotNr.get();
}
public String getWareNr() {
return wareNr.get();
}
public String getServiceDescription() {
return serviceDescription.get();
}
public String getProductionLineNr() {
return productionLineNr.get();
}
int getOrderProductionNr() {return orderProductionNr.get();}
}
`
I have tried using a callback but I have never used callbacks before and don't properly understand how I can fit my needs into a callback. Any help will be important to my learning. Thanks SO.
You have to define a custom TableCell for your status column like this:
public class ColoredStatusTableCell extends TableCell<TableRow, Status> {
#Override
protected void updateItem(Status item, boolean empty) {
super.updateItem(item, empty);
if (empty || getTableRow() == null) {
setText(null);
setGraphic(null);
} else {
TableRow row = (TableRow) getTableRow().getItem();
setText(item.toString());
setStyle("-fx-background-color: " + row.getColorAsString());
// If the statis is changing dynamic you have to add the following:
row.statusProperty()
.addListener((observable, oldValue, newValue) ->
setStyle("-fx-background-color: " + row.getColorAsString()));
}
}
}
Where TableRow:
public class TableRow {
private ObjectProperty<Status> status;
private Map<Status, Color> statusColor;
public TableRow(Status status, Map<Status, Color> statusColor) {
this.status = new SimpleObjectProperty<>(status);
this.statusColor = statusColor;
}
public Status getStatus() {
return status.get();
}
public ObjectProperty<Status> statusProperty() {
return status;
}
public Color getStatusColor() {
return statusColor.get(status.get());
}
public String getColorAsString() {
return String.format("#%02X%02X%02X",
(int) (getStatusColor().getRed() * 255),
(int) (getStatusColor().getGreen() * 255),
(int) (getStatusColor().getBlue() * 255));
}
}
Status:
public enum Status {
READY, STARTED, DONE
}
and the controller:
public class TestController {
#FXML
private TableView<TableRow> table;
#FXML
private TableColumn<TableRow, Status> column;
private ObservableList<TableRow> data = FXCollections.observableArrayList();
#FXML
public void initialize() {
column.setCellValueFactory(data -> data.getValue().statusProperty());
column.setCellFactory(factory -> new ColoredStatusTableCell());
Map<Status, Color> statusColor = new HashMap<>();
statusColor.put(Status.READY, Color.GREEN);
statusColor.put(Status.STARTED, Color.YELLOW);
statusColor.put(Status.DONE, Color.GRAY);
TableRow ready = new TableRow(Status.READY, statusColor);
TableRow started = new TableRow(Status.STARTED, statusColor);
TableRow done = new TableRow(Status.DONE, statusColor);
data.addAll(ready, started, done);
table.setItems(data);
}
}
I chose to set the status as an enum because it is easier to handle it,
then I have used a map to each status-color combination, then in the cell you can set its background color to the matched color of the status.
If you want of course instead of Color.YELLOW and so on you can use a custom Color.rgb(red,green,blue)
I finally found the solution without having to use any extra classes, just a callback in my controller class with the help of this SO link:
StackOverFlow Link
`
private void populateTable() {
log.appLog("Populating table\r\n");
//clmStatus.setCellValueFactory(new PropertyValueFactory<>("status"));
clmStatus.setCellFactory(new Callback<TableColumn<Orders, String>,
TableCell<Orders, String>>()
{
#Override
public TableCell<Orders, String> call(
TableColumn<Orders, String> param) {
return new TableCell<Orders, String>() {
#Override
protected void updateItem(String item, boolean empty) {
if (!empty) {
int currentIndex = indexProperty()
.getValue() < 0 ? 0
: indexProperty().getValue();
String clmStatus = param
.getTableView().getItems()
.get(currentIndex).getStatus();
if (clmStatus.equals("READY")) {
setTextFill(Color.WHITE);
setStyle("-fx-font-weight: bold");
setStyle("-fx-background-color: green");
setText(clmStatus);
} else if (clmStatus.equals("STARTED")){
setTextFill(Color.BLACK);
setStyle("-fx-font-weight: bold");
setStyle("-fx-background-color: yellow");
setText(clmStatus);
} else if (clmStatus.equals("DONE")){
setTextFill(Color.BLACK);
setStyle("-fx-font-weight: bold");
setStyle("-fx-background-color: gray");
setText(clmStatus);
} else {
setTextFill(Color.WHITE);
setStyle("-fx-font-weight: bold");
setStyle("-fx-background-color: red");
setText(clmStatus);
}
}
}
};
}
});
clmStartDateTime.setCellValueFactory(new PropertyValueFactory<>("startDateTime"));
clmShopOrder.setCellValueFactory(new PropertyValueFactory<>("extra1"));
clmRotation.setCellValueFactory(new PropertyValueFactory<>("batchLotNr"));
clmGMIECode.setCellValueFactory(new PropertyValueFactory<>("wareNr"));
clmSAPCode.setCellValueFactory(new PropertyValueFactory<>("serviceDescription"));
clmLineName.setCellValueFactory(new PropertyValueFactory<>("productionLineNr"));
clmOrderProductionNr.setCellValueFactory(new PropertyValueFactory<>("orderProductionNr"));
tblOrders.setItems(list);
}
`
I don't have badge to comment, but wanted to add some details.
I wanted to format color of cell based on the boolean value which i have in my data set. I have reviewed this question and similar one provided already here:
Stackoverflow link - style based on another cell in row
What was missing in both for me is reseting style when there is no value as kleopatra mentioned.
This works for me:
public class TableCellColored extends TableCell<DimensionDtoFxBean, DimValVoFxBean> {
private static final String DEFAULT_STYLE_CLASS = "table-cell";
public TableCellColored() {
super();
}
#Override
protected void updateItem(DimValVoFxBean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("");
resetStyle();
return;
}
setText(Optional.ofNullable(item.getValue()).map(BigDecimal::toString).orElse(""));
Boolean conversionFlag = Optional.ofNullable(item.getConversionFlag()).orElse(true);
updateStyle(conversionFlag);
item.conversionFlagProperty()
.addListener((observable, oldValue, newValue) -> updateStyle(newValue));
}
private void updateStyle(Boolean conversionFlag) {
if (!conversionFlag) {
setStyle("-fx-background-color: red");
} else {
resetStyle();
}
}
private void resetStyle() {
setStyle("");
getStyleClass().addAll(TableCellColored.DEFAULT_STYLE_CLASS);
}
}
Since I have value object with value and boolean flag I can do it i seperate class and don't have add lambda in controller.
Deafult styling of cell is transparent so if we use style to change color, we have to reset it when there is no value.
Since direct styling has bigger priority than class it overrides default styling from css classes.
To be on the safe side I also apply DEFAULT_STYLE_CLASS. Value taken from TableCell class.
Without listener and styles reset I red was staying in table during scrolling. After few scrolls all cells where red. So listener and styles reset is the must have for me.

JavaFx Create Table Cell Accepts numbers only?

I have TableView with column inside it that must only accept numbers.
and I added onMouseClickListener to enter edit mode on the mouse click instead of double click on the cell
I want a way to not allowing the user to enter any character except numbers. My code is:
Callback<TableColumn<DailyDetails, String>, TableCell<DailyDetails, String>> defaultCellFactory
= TextFieldTableCell.<DailyDetails>forTableColumn();
dailyCredit.setCellFactory(column -> {
TableCell<DailyDetails, String> cell = defaultCellFactory.call(column);
cell.setOnMouseClicked(e -> {
if (!cell.isEditing() && !cell.isEmpty()) {
cell.getTableView().edit(cell.getIndex(), column);
}
});
return cell;
});
I implemented Table cell from the scratch:
class NumberCell extends TableCell<DailyDetails, String> {
private TextField textField;
public NumberCell() {
}
#Override
public void startEdit() {
super.startEdit();
if (textField == null) {
createTextField();
}
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.selectAll();
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText(String.valueOf(getItem()));
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setText(getString());
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
private void createTextField() {
textField = new TextField(getString());
//textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.lengthProperty().addListener(new ChangeListener<Number>(){
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
if (newValue.intValue() > oldValue.intValue()) {
char ch = textField.getText().charAt(oldValue.intValue());
// Check if the new character is the number or other's
if (!(ch >= '0' && ch <= '9' )) {
// if it's not number then just setText to previous one
textField.setText(textField.getText().substring(0,textField.getText().length()-1));
}
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
Callback<TableColumn<DailyDetails, String>,
TableCell<DailyDetails, String>> cellFactory
= (TableColumn<DailyDetails, String> p) -> new NumberCell();
dailyDebit.setCellFactory(cellFactory);
the problem is i lost the on mouse listener cell.setOnMouseClicked!!!
how do i get the cell again to assign the listener ???
Just for driving the new api into everybody's brain: a full example with a slightly different TextFormatter (than in the other answer) that is Locale-aware and (dirtily!) hooked into core TextFieldTableCell, can be used in any custom editing TableCell as well:
/**
* Example of how-to use a TextFormatter in a editing TableCell.
*/
public class CellFormatting extends Application {
private Parent getContent() {
ObservableList<IntData> data = FXCollections.observableArrayList(
new IntData(1), new IntData(2), new IntData(3)
);
TableView<IntData> table = new TableView<>(data);
table.setEditable(true);
TableColumn<IntData, Integer> column = new TableColumn<>("Data");
column.setCellValueFactory(new PropertyValueFactory("data"));
// core default: will throw exception on illegal values
// column.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
NumberFormat format = NumberFormat.getIntegerInstance();
UnaryOperator<TextFormatter.Change> filter = c -> {
if (c.isContentChange()) {
ParsePosition parsePosition = new ParsePosition(0);
// NumberFormat evaluates the beginning of the text
format.parse(c.getControlNewText(), parsePosition);
if (parsePosition.getIndex() == 0 ||
parsePosition.getIndex() < c.getControlNewText().length()) {
// reject parsing the complete text failed
return null;
}
}
return c;
};
column.setCellFactory(c -> new ValidatingTextFieldTableCell<>(
// note: each cell needs its own formatter
// see comment by #SurprisedCoconut
new TextFormatter<Integer>(
// note: should use local-aware converter instead of core!
new IntegerStringConverter(), 0,
filter)));
table.getColumns().add(column);
VBox box = new VBox(table);
return box;
}
/**
* TextFieldTableCell that validates input with a TextFormatter.
* <p>
* Extends TextFieldTableCell, accesses super's private field reflectively.
*
*/
public static class ValidatingTextFieldTableCell<S, T> extends TextFieldTableCell<S, T> {
private TextFormatter<T> formatter;
private TextField textAlias;
public ValidatingTextFieldTableCell() {
this((StringConverter<T>)null);
}
public ValidatingTextFieldTableCell(StringConverter<T> converter) {
super(converter);
}
public ValidatingTextFieldTableCell(TextFormatter<T> formatter) {
super(formatter.getValueConverter());
this.formatter = formatter;
}
/**
* Overridden to install the formatter. <p>
*
* Beware: implementation detail! super creates and configures
* the textField lazy on first access, so have to install after
* calling super.
*/
#Override
public void startEdit() {
super.startEdit();
installFormatter();
}
private void installFormatter() {
if (formatter != null && isEditing() && textAlias == null) {
textAlias = invokeTextField();
textAlias.setTextFormatter(formatter);
}
}
private TextField invokeTextField() {
Class<?> clazz = TextFieldTableCell.class;
try {
Field field = clazz.getDeclaredField("textField");
field.setAccessible(true);
return (TextField) field.get(this);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
public static class IntData {
IntegerProperty data = new SimpleIntegerProperty(this, "data");
public IntData(int value) {
setData(value);
}
public void setData(int value) {
data.set(value);
}
public int getData() {
return data.get();
}
public IntegerProperty dataProperty() {
return data;
}
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent()));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
BTW, the formatter is re-used from another question where the task at hand was to restrict input into a Spinner.
Use a TextFormatter on the TextField like this:
TextFormatter<String> formatter = new TextFormatter<String>( change -> {
change.setText(change.getText().replaceAll("[^0-9.,]", ""));
return change;
});
textField.setTextFormatter(formatter);
Works with Java8u40 upwards. Use e. g. the TableView example from the Oracle site as base.

Implementing an ObservableValue

I have this object:
public class Oggetto{
private int value;
private boolean valid;
public Oggetto(int value, boolean valid) {
this.value = value;
this.valid = valid;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
}
and I would like implement an Observable object that fires events when something inside changes
Here the observable object:
public class OggettoOsservabile implements ObservableValue<Oggetto>{
private Oggetto value;
OggettoOsservabile(int i, boolean b) {
this.value=new Oggetto(i, b);
}
#Override
public void addListener(ChangeListener<? super Oggetto> listener) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public void removeListener(ChangeListener<? super Oggetto> listener) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public Oggetto getValue() {
return value;
}
#Override
public void addListener(InvalidationListener listener) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public void removeListener(InvalidationListener listener) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
i dont know how to proceed in order to detect a change in the class "Oggetto" and send a notification to the registeres listener.
OggettoOsservabile oggetto = new OggettoOsservabile(1, false);
oggetto.addListener(new ChangeListener<Oggetto>() {
public void changed(ObservableValue<? extends Oggetto> observable, Oggetto oldValue, Oggetto newValue) {
System.out.println("changed " + oldValue + "->" + newValue);
}
});
Implement your Oggetto class using standard JavaFX Properties:
import javafx.beans.property.BooleanProperty ;
import javafx.beans.property.IntegerProperty ;
import javafx.beans.property.SimpleBooleanProperty ;
import javafx.beans.property.SimpleIntegerProperty ;
public class Oggetto {
private final IntegerProperty value = new SimpleIntegerProperty() ;
public final IntegerProperty valueProperty() {
return value ;
}
public final int getValue() {
return value.get();
}
public final void setValue(int value) {
this.value.set(value);
}
private final BooleanProperty valid = new SimpleBooleanProperty();
public final BooleanProperty validProperty() {
return valid ;
}
public final boolean isValid() {
return valid.get();
}
public final void setValid(boolean valid) {
this.valid.set(valid);
}
public Oggetto(int value, boolean valid) {
setValue(value);
setValid(valid);
}
}
This may be all you need, as you can just observe the individual properties. But if you want a class that notifies invalidation listeners if either property changes, you can extend ObjectBinding:
import javafx.beans.binding.ObjectBinding ;
public class OggettoObservable extends ObjectBinding {
private final Oggetto value ;
public OggettoObservable(int value, boolean valid) {
this.value = new Oggetto(value, valid);
bind(this.value.valueProperty(), this.value.validProperty());
}
#Override
public Oggetto computeValue() {
return value ;
}
}
import javafx.beans.InvalidationListener;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
public class VerySimply implements ObservableValue<Integer> {
private int newValue;
public ChangeListener<Integer> listener = new ChangeListener<Integer>() {
#Override
public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue) {
System.out.println(" :) "+ newValue.intValue());
}
};
#Override
public void addListener(ChangeListener<? super Integer> listener) {
}
#Override
public void removeListener(ChangeListener<? super Integer> listener) {
}
#Override
public Integer getValue() {
return newValue;
}
#Override
public void addListener(InvalidationListener listener) {
}
#Override
public void removeListener(InvalidationListener listener) {
}
public void setNewValue(int newValue) {
int oldValue = this.newValue;
this.newValue = newValue;
listener.changed(this,oldValue,this.newValue);
}
}

Why all rows in TableView have checkbox?

I creating project for my job. I need to use tableview and user need to check data going to database.
I was found how to add checkbox to TableView from this post
https://stackoverflow.com/a/7973514/3037869
This is working for me but my TableView now looking
How to fix this checkbox spam?
My code
public class ProductPSController extends BorderPane{
#FXML public TableColumn<ProductPS, String> produkt;
#FXML public TableColumn<ProductPS, String> symbol;
#FXML public TableColumn<ProductPS, String> atrybuty;
#FXML public TableColumn<ProductPS, Integer> id;
#FXML public TableColumn<ProductPS, Integer> stock;
#FXML public TableColumn<ProductPS, Integer> count;
#FXML public TableColumn<ProductPS, Integer> price;
#FXML public TableColumn<ProductPS, Boolean> checkbox;
#FXML public TableView <ProductPS> tab;
#FXML public BorderPane produkty;
public ObservableList<ProductPS> data = FXCollections.observableArrayList();
public ProductPSController()
{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("product.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
id.prefWidthProperty().bind(tab.widthProperty().multiply(0.10));
stock.prefWidthProperty().bind(tab.widthProperty().multiply(0.10));
produkt.prefWidthProperty().bind(tab.widthProperty().multiply(0.20));
symbol.prefWidthProperty().bind(tab.widthProperty().multiply(0.10));
atrybuty.prefWidthProperty().bind(tab.widthProperty().multiply(0.30));
count.prefWidthProperty().bind(tab.widthProperty().multiply(0.10));
price.prefWidthProperty().bind(tab.widthProperty().multiply(0.10));
tab.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
setProduct();
}
public void setProduct()
{
id.setCellValueFactory(new PropertyValueFactory<ProductPS, Integer>("id"));
symbol.setCellValueFactory(new PropertyValueFactory<ProductPS, String>("symbol"));
stock.setCellValueFactory(new PropertyValueFactory<ProductPS, Integer>("id_stock"));
produkt.setCellValueFactory(new PropertyValueFactory<ProductPS, String>("product_name"));
atrybuty.setCellValueFactory(new PropertyValueFactory<ProductPS, String>("attributes"));
count.setCellValueFactory(new PropertyValueFactory<ProductPS, Integer>("count"));
Callback<TableColumn<ProductPS, Boolean>, TableCell<ProductPS, Boolean>> booleanCellFactory =
new Callback<TableColumn<ProductPS, Boolean>, TableCell<ProductPS, Boolean>>() {
#Override
public TableCell<ProductPS, Boolean> call(TableColumn<ProductPS, Boolean> p) {
return new BooleanCell();
}
};
price.setCellValueFactory(new PropertyValueFactory<ProductPS, Integer>("price"));
checkbox.setCellValueFactory(new PropertyValueFactory<ProductPS, Boolean>("checkbox"));
checkbox.setCellFactory(booleanCellFactory);
checkbox.setEditable(true);
try(Connection c = MysqlConnect.getConnection())
{
String SQL = "";
ResultSet rs = c.createStatement().executeQuery(SQL);
while(rs.next()){
data.add(new ProductPS(rs.getInt("id_product"),rs.getInt("id_stock_available"),rs.getString("name"),rs.getString("atrybuty"),rs.getInt("quantity"),rs.getFloat("price"),rs.getString("reference")));
}
c.close();
} catch (SQLException e) {
System.out.println(e.toString());
// TODO Auto-generated catch block
}
for(int i=0; i<data.size(); i++) {
if(i+1<data.size() && data.get(i).getAttributes().length()==0 && data.get(i).getId()==data.get(i+1).getId() ){data.remove(i);}
}
tab.setItems(data);
}
class BooleanCell extends TableCell<ProductPS, Boolean> {
private CheckBox checkBox;
public BooleanCell() {
checkBox = new CheckBox();
checkBox.setDisable(false);
checkBox.selectedProperty().addListener(new ChangeListener<Boolean> () {
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if(isEditing())
{
commitEdit(newValue == null ? false : newValue);
}
}
});
this.setGraphic(checkBox);
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
this.setEditable(true);
}
#Override
public void startEdit() {
super.startEdit();
if (isEmpty()) {
return;
}
checkBox.setDisable(false);
checkBox.requestFocus();
}
#Override
public void cancelEdit() {
super.cancelEdit();
checkBox.setDisable(true);
}
public void commitEdit(Boolean value) {
super.commitEdit(value);
checkBox.setDisable(true);
}
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (!isEmpty()) {
checkBox.setSelected(item);
}
}
}
}
Thx for help and say what i done bad.
In BooleanCell, you need to set the graphic to checkBox if the cell is not empty, and set it to null if it is empty.
Remove the line
this.setGraphic(checkBox);
from BooleanCell's constructor, and change updateItem(...) to:
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
checkBox.setSelected(item);
setGraphic(checkBox);
}
}

How to use WeakChangeListener with JavaFx?

I wrote my TableCell implementation based on TableCell. I'm using ChangeListener, but it is not garbage collected. How to use WeakChangeListener in this case. Please see my code. How to switch it to WeakChangeListener.
changeListener = new ChangeListener<Object[]>() {
#Override
public void changed(ObservableValue<? extends Object[]> observable, Object[] oldValue, Object[] newValue) {
if (newValue != null && oldValue != null) {
if (oldValue[21].equals(newValue[21])) {
if (newValue[updateBasedValues] != null) {
if (!newValue[updateBasedValues].equals(oldValue[updateBasedValues])) {
justUpdated = true;
}
}
}
}
}
};
itemProperty().addListener(changeListener);
You need to keep a reference to the weak listener so that it is not garbage collected too soon.
Read this very good discussion on the oracle forum : https://community.oracle.com/thread/2396063
I've written an adapter class "WeakAdapter" that you can use or extend.
Example on how to use this class:
Instead of writing
myBooleanProperty.addListener(new ChangeListener<Boolean> () {...});
use:
weak = new WeakAdapter();
weak.addChangeListener(myBooleanProperty, new ChangeListener<Boolean> (){});
Here is the code of class WeakAdapter:
public class WeakAdapter {
ArrayList<Object> listenerRefs = new ArrayList<>();
public WeakAdapter() {
}
public void dipose() {
listenerRefs.clear();
}
public final <T> void remove(ChangeListener<T> listener) {
listenerRefs.remove(listener);
}
public final <T> void addChangeListener(final ObservableValue observable, ChangeListener<T> listener) {
listenerRefs.add(listener);
observable.addListener(new WeakChangeListener<>(listener));
}
public final <T> WeakListChangeListener<T> addListChangeListener(ListChangeListener<T> listener) {
listenerRefs.add(listener);
return new WeakListChangeListener<>(listener);
}
public void addInvalidationListener(final Observable listened, InvalidationListener listener) {
listenerRefs.add(listener);
listened.addListener(new WeakInvalidationListener(listener));
}
public final void stringBind(final StringProperty propertyToUpdate, final StringExpression expressionToListen) {
ChangeListener<String> listener = new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> ov, String t, String name) {
propertyToUpdate.set(name);
}
};
listenerRefs.add(listener);
expressionToListen.addListener(new WeakChangeListener<>(listener));
listener.changed(null, null, expressionToListen.get());
}
public final void booleanBind(final BooleanProperty propertyToUpdate, final BooleanExpression expressionToListen) {
ChangeListener<Boolean> listener = new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean name) {
propertyToUpdate.set(name);
}
};
listenerRefs.add(listener);
expressionToListen.addListener(new WeakChangeListener<>(listener));
propertyToUpdate.set(expressionToListen.get());
}
}

Resources