I am trying to insert HyperLink with text "Remove" in all rows of a column. Only TabelView row will be inserted when clicked on a button. The hyperlink also get inserted but not for all rows. It automatically get blank cell for previous row if next row data is added. Screenshot:
The hyperlink listener will be created to remove selected row if clicked on it.
This method is called when user click on a button, here i am creating the link:
public void SalesAdd(ActionEvent actionEvent){
if(quantity.getText().isEmpty()){
quantity.setStyle("-fx-border-color: red");
return;
}
String name = comboBox.getSelectionModel().getSelectedItem();
String batch = batchno.getText();
String exp = expDate.getText();
int qty = Integer.parseInt(quantity.getText());
Double mrp1 = Double.valueOf(mrp.getText());
Double amt = Double.valueOf(amount.getText());
Double mrpAmount = mrp1*qty;
PropertyValueFactory<TableData,String> namePro = new PropertyValueFactory<TableData,String>("name");
PropertyValueFactory<TableData,Integer> qtyPro = new PropertyValueFactory<TableData,Integer>("qty");
PropertyValueFactory<TableData,String> expPro = new PropertyValueFactory<TableData,String>("exp");
PropertyValueFactory<TableData,String> batchPro = new PropertyValueFactory<TableData,String>("batch");
PropertyValueFactory<TableData,Double> mrpPro = new PropertyValueFactory<TableData,Double>("mrp");
PropertyValueFactory<TableData,Double> amtPro = new PropertyValueFactory<TableData,Double>("amt");
PropertyValueFactory<TableData,Hyperlink> rmbutton = new PropertyValueFactory<TableData,Hyperlink>("rbutton");
nameColumn.setCellValueFactory(namePro);
qtyColumn.setCellValueFactory(qtyPro);
expColumn.setCellValueFactory(expPro);
batchColumn.setCellValueFactory(batchPro);
mrpColumn.setCellValueFactory(mrpPro);
amtColumn.setCellValueFactory(amtPro);
removeRowColumn.setCellValueFactory(rmbutton);
for(TableData data:tableData){
if(data.getName()==comboBox.getEditor().getText() || data.getName() == comboBox.getSelectionModel().getSelectedItem().toString()){
common.dialogAlert("Already in Table!","Already in the Table!","Already Exist,Please change the quantity!");
return;
}
}
tableData.add(new TableData(name,batch,exp,qty,mrp1,mrpAmount, rbutton));
tableView.setItems(tableData);
clearInput();
calctotal();
}
The TableData Class:
public class TableData extends ActionEvent {
private final SimpleStringProperty name;
private final SimpleStringProperty batch;
private final SimpleStringProperty exp;
private final SimpleIntegerProperty qty;
private final SimpleDoubleProperty mrp;
private final SimpleDoubleProperty amt;
private final Hyperlink rbutton;
public Hyperlink getRbutton() {
return rbutton;
}
public TableData(String name, String batch,
String exp, int qty, Double mrp, Double amt, Hyperlink rbutton) {
this.name = new SimpleStringProperty(name);
this.batch = new SimpleStringProperty(batch);
this.exp = new SimpleStringProperty(exp);
this.qty = new SimpleIntegerProperty(qty);
this.mrp = new SimpleDoubleProperty(mrp);
this.amt = new SimpleDoubleProperty(amt);
this.rbutton = rbutton;
this.amt.bind(this.qty.multiply(this.mrp));
}
public String getName() {
return name.get();
}
public SimpleStringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public String getBatch() {
return batch.get();
}
public SimpleStringProperty batchProperty() {
return batch;
}
public void setBatch(String batch) {
this.batch.set(batch);
}
public String getExp() {
return exp.get();
}
public SimpleStringProperty expProperty() {
return exp;
}
public void setExp(String exp) {
this.exp.set(exp);
}
public int getQty() {
return qty.get();
}
public SimpleIntegerProperty qtyProperty() {
return qty;
}
public void setQty(int qty) {
this.qty.set(qty);
}
public double getMrp() {
return mrp.get();
}
public SimpleDoubleProperty mrpProperty() {
return mrp;
}
public void setMrp(double mrp) {
this.mrp.set(mrp);
}
public double getAmt() {
return amt.get();
}
public SimpleDoubleProperty amtProperty() {
return amt;
}
public void setAmt(double amt) {
this.amt.set(amt);
}
}
How do i add this same HyperLink for every row in a column?
Including UI elements in the item class is seldom a good idea (also extending ActionEvent seems unnecessary). The link works independent form any item value, therefore it should't use one. Instead use a cell that displays a link when it's non-empty:
public class RemoveCell<T> extends TableCell<T, Void> {
private final Hyperlink link;
public RemoveCell() {
link = new Hyperlink("Remove");
link.setOnAction(evt -> {
// remove row item from tableview
getTableView().getItems().remove(getTableRow().getIndex());
});
}
#Override
protected void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
setGraphic(empty ? null : link);
}
}
This cell does not take any data. Instead the link is displayed only for non-empty cells (setGraphic(empty ? null : link);). When the onAction event of the Hyperlink is triggered, the data available from the TableCell is used to remove the corresponding element from the TableView that contains the cell. Additional code could be added to the body of the lambda expression in case additional operations need to be done on the removal of a item.
Do not use a cellValueFactory for the removeRowColumn (choosing Void as value type only allows for null values), instead just use a cellFactory creating RemoveCells:
removeRowColumn.setCellFactory(tc -> new RemoveCell<>());
BTW: You seem to be recreating the cellValueFactorys on a button click inserting a single new item. It would be a better idea to do this just once for the whole table instead of once per inserted table row.
Related
I'm new to java and don't fully understand the structure of Javafx, so I'm getting lost in the weeds here.
It's an inventory management app for a course I'm taking.
I have two classes that are inheriting from an abstract class, and an ObservableList that holds instances of both the child classes. Now, when I try to display both classes together in TableView with a cellvaluefactory, I get errors due to the missing attributes from one or the other. The program compiles but the warnings are due to fields "missing" from whichever field is missing from it's respective instance. (PartOutsourced is missing the "machineId" field, and PartInhouse is missing the "companyName" field.
UI picture
I know there must be some way to get the cellValueFactory to ignore the null values or fill them with empty strings, but I have no idea how, and google has been a dead end.
Here are the two child classes:
public class PartOutsourced extends Part {
private String companyName;
public PartOutsourced(String name, int stock, double price, int min, int max, String companyName) {
super(name, stock, price, min, max);
this.companyName = companyName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
}
public class PartInHouse extends Part {
private int machineId;
public PartInHouse(String name, int stock, double price, int min, int max, int machineId) {
super(name, stock, price, min, max);
this.machineId = machineId;
}
public int getMachineId() {
return machineId;
}
public void setMachineId(int machineId) {
this.machineId = machineId;
}
}
Here is the cellValueFactory code:
col_id.setCellValueFactory(new PropertyValueFactory<Part, String>("id"));
col_name.setCellValueFactory(new PropertyValueFactory<Part, String>("name"));
col_stock.setCellValueFactory(new PropertyValueFactory<Part, String>("stock"));
col_price.setCellValueFactory(new PropertyValueFactory<Part, String>("price"));
col_min.setCellValueFactory(new PropertyValueFactory<Part, String>("min"));
col_max.setCellValueFactory(new PropertyValueFactory<Part, String>("max"));
col_machine_id.setCellValueFactory(new PropertyValueFactory<Part, String>("machineId"));
col_company.setCellValueFactory(new PropertyValueFactory<Part, String>("companyName"));
A bit of the the stacktrace:
May 14, 2020 12:14:06 PM javafx.scene.control.cell.PropertyValueFactory getCellDataReflectively
WARNING: Can not retrieve property 'machineId' in PropertyValueFactory: javafx.scene.control.cell.PropertyValueFactory#5f88bef3 with provided class type: class inventory.datamodel.PartOutsourced
java.lang.IllegalStateException: Cannot read from unreadable property machineId
Use your own implementation of the Callback (e.g. using lambdas) instead of the PropertyValueFactory (this is generally recommended anyway; PropertyValueFactory was really only written as a placeholder before lambda expression were introduced, since implementing the Callback with an inner class was very verbose). Then you can return the property you need (e.g. an IntegerProperty wrapping the value) if the object is the right type, or a property wrapping null (e.g. an ObjectProperty<Number>) if not.
Since your machineId is a Number, you should use
private TableColumn<Part, Number> col_machine_id ; // note: please use proper Java naming conventions
and then you can do
col_machine_id.setCellValueFactory(cellData -> {
Part part = cellData.getValue() ;
if (part instanceof PartInHouse) {
return new SimpleIntegerProperty(((PartInHouse)part).getMachineId());
}
return new SimpleObjectProperty<>(null);
});
Note that it's also recommended to use JavaFX properties in your model class:
public class PartInHouse extends Part {
private final IntegerProperty machineId = new SimpleIntegerProperty();
public PartInHouse(String name, int stock, double price, int min, int max, int machineId) {
super(name, stock, price, min, max);
setMachineId(machineId);
}
public IntegerProperty machineIdProperty() {
return machineId ;
}
public final int getMachineId() {
return machineIdProperty().get();
}
public final void setMachineId(int machineId) {
machineIdProperty().set(machineId);
}
}
in which case you can use the following cell value factory:
col_machine_id.setCellValueFactory(cellData -> {
Part part = cellData.getValue() ;
if (part instanceof PartInHouse) {
return ((PartInHouse)part).machineIdProperty();
}
return new SimpleObjectProperty<>(null);
});
Here's a complete running example:
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class InheritanceTableModelTest extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TableView<Part> table = new TableView<>();
for (int i = 1 ; i <= 20 ; i++) {
table.getItems().add(new PartInHouse("Part "+(2*i), i));
table.getItems().add(new PartOutsourced("Part "+(2*i+1), "Company "+i));
}
TableColumn<Part, String> nameCol = new TableColumn<>("Part");
TableColumn<Part, Number> machineIdCol = new TableColumn<>("Machine ID");
TableColumn<Part, String> companyCol = new TableColumn<>("Company");
nameCol.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
machineIdCol.setCellValueFactory(cellData -> {
Part part = cellData.getValue();
if (part instanceof PartInHouse) {
return ((PartInHouse)part).machineIdProperty();
}
return new SimpleObjectProperty<>(null);
});
companyCol.setCellValueFactory(cellData -> {
Part part = cellData.getValue();
if (part instanceof PartOutsourced) {
return ((PartOutsourced)part).companyNameProperty();
}
return new SimpleObjectProperty<>(null);
});
table.getColumns().add(nameCol);
table.getColumns().add(machineIdCol);
table.getColumns().add(companyCol);
Scene scene = new Scene(new BorderPane(table));
primaryStage.setScene(scene);
primaryStage.show();
}
public static class Part {
private final StringProperty name = new SimpleStringProperty();
public Part(String name) {
setName(name);
}
public StringProperty nameProperty() {
return name;
}
public final String getName() {
return nameProperty().get();
}
public final void setName(String name) {
nameProperty().set(name);
}
}
public static class PartInHouse extends Part {
private final IntegerProperty machineId = new SimpleIntegerProperty() ;
public PartInHouse(String name, int machineId) {
super(name);
setMachineId(machineId);
}
public IntegerProperty machineIdProperty() {
return machineId ;
}
public int getMachineId() {
return machineIdProperty().get();
}
public void setMachineId(int machineId) {
machineIdProperty().set(machineId);
}
}
public static class PartOutsourced extends Part {
private final StringProperty companyName = new SimpleStringProperty();
public PartOutsourced(String name, String companyName) {
super(name);
setCompanyName(companyName);
}
public StringProperty companyNameProperty() {
return companyName ;
}
public String getCompanyName() {
return companyNameProperty().get();
}
public void setCompanyName(String companyName) {
companyNameProperty().set(companyName);
}
}
public static void main(String[] args) {
Application.launch(args);
}
}
I have an issue with JavaFX TableView UI update. After I change the observable object, it does not update the UI of TableView. But if I perform a magical ritual of pulling TableView's scroll bar down and up again - it seems to redraw the table and update items in it.
Through debugging I've ensured, that the PreferencesSet ArrayList and object are updated correctly.
Here's gif demonstration of what is happening
This is my first time asking a question here, so I could have left out some important info. Feel free to ask me for it. Thank you in advance.
Here's code (I have left out unrelated stuff):
ControllerClass:
public class TestSomethingController implements Initializable {
public TableView<PreferenceValues.PreferencesSet> preferencesTable;
public TableColumn mdColumn;
public TableColumn typeColumn;
public TableColumn tradeColumn;
public TableColumn plastColumn;
public TableColumn capColumn;
public TableColumn multColumn;
public TableColumn sizeColumn;
#Override
public void initialize(URL location, ResourceBundle resources) {
setNorthPanel();
setTableColumns();
fillAllInfo();
}
private void setTableColumns() {
mdColumn.setCellValueFactory(new PropertyValueFactory<PreferenceValues.PreferencesSet, MarketDirection>("md"));
typeColumn.setCellValueFactory(new PropertyValueFactory<PreferenceValues.PreferencesSet, UserOfferType>("type"));
tradeColumn.setCellValueFactory(new PropertyValueFactory<PreferenceValues.PreferencesSet, Boolean>("trade"));
plastColumn.setCellValueFactory(new PropertyValueFactory<PreferenceValues.PreferencesSet, Long>("plast"));
capColumn.setCellValueFactory(new PropertyValueFactory<PreferenceValues.PreferencesSet, Double>("cap"));
multColumn.setCellValueFactory(new PropertyValueFactory<PreferenceValues.PreferencesSet, Double>("mult"));
sizeColumn.setCellValueFactory(new PropertyValueFactory<PreferenceValues.PreferencesSet, Long>("size"));
}
private void fillAllInfo() {
preferencesTable.setItems(FXCollections.observableArrayList(CurrentSession.currentUser.getPreferencesList()));
fillNorthPanel();
}
public void applyClicked(ActionEvent actionEvent) {
applyNorthPanelChanges();
}
private void applyNorthPanelChanges() {
PreferenceValues.PreferencesSet preferencesSet = CurrentSession.currentUser.getPreferencesSet(dirChoiceBox.getSelectionModel().getSelectedItem(), offerTypeChoiceBox.getSelectionModel().getSelectedItem());
preferencesSet.setTrade(tradeCheckBox.isSelected());
preferencesSet.setPlast(plastSpinner.getValue());
preferencesSet.setCap(capRateSpinner.getValue());
preferencesSet.setMult(multSpinner.getValue());
preferencesSet.setSize(sizeSpinner.getValue());
preferencesSet.savePreferences();
}
User class:
public class User {
private PreferenceValues preferenceValues;
public PreferenceValues.PreferencesSet getPreferencesSet(MarketDirection md, UserOfferType userOfferType) {
return preferenceValues.getPreferencesSet(md, userOfferType);
}
public ArrayList<PreferenceValues.PreferencesSet> getPreferencesList() {
return preferenceValues.getPreferencesList();
}
}
PreferenceValues class:
import java.util.ArrayList;
import java.util.TreeMap;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
public class PreferenceValues {
private Preferences preferences;
private ArrayList<PreferencesSet> preferencesList;
private TreeMap<String, PreferencesSet> preferencesMap;
public PreferenceValues(User user) {
preferencesList = new ArrayList<>();
preferencesMap = new TreeMap<>();
preferences = Preferences.userRoot().node("prefexample" + user.getwmId());
for (MarketDirection md : MarketDirection.values()) {
for (UserOfferType userOfferType : UserOfferType.values()) {
if (userOfferType != UserOfferType.UNDEF) {
PreferencesSet preferencesSet = new PreferencesSet(md, userOfferType, preferences);
preferencesList.add(preferencesSet);
preferencesMap.put(md.toString() + userOfferType.toString(), preferencesSet);
}
}
}
}
protected ArrayList<PreferencesSet> getPreferencesList() {
return preferencesList;
}
private String getMapKey(MarketDirection md, UserOfferType userOfferType) {
return md.toString() + userOfferType.toString();
}
protected PreferencesSet getPreferencesSet(MarketDirection md, UserOfferType userOfferType) {
return preferencesMap.get(getMapKey(md, userOfferType));
}
public void clear() throws BackingStoreException {
preferences.clear();
}
public class PreferencesSet {
Preferences preferences;
private MarketDirection md;
private UserOfferType type;
private boolean trade;
private int plast;
private double cap;
private double mult;
private int size;
public PreferencesSet(MarketDirection md, UserOfferType type, Preferences preferences) {
this.md = md;
this.type = type;
this.preferences = preferences;
trade = preferences.node(md.toString()).node(type.toString()).getBoolean("trade", false);
plast = preferences.node(md.toString()).node(type.toString()).getInt("plast", 222);
cap = preferences.node(md.toString()).node(type.toString()).getDouble("cap", 333);
mult = preferences.node(md.toString()).node(type.toString()).getDouble("mult", 1);
size = preferences.node(md.toString()).node(type.toString()).getInt("size", 15000);
}
public void savePreferences() {
preferences.node(md.toString()).node(type.toString()).putBoolean("trade", trade);
preferences.node(md.toString()).node(type.toString()).putInt("plast", plast);
preferences.node(md.toString()).node(type.toString()).putDouble("cap", cap);
preferences.node(md.toString()).node(type.toString()).putDouble("mult", mult);
preferences.node(md.toString()).node(type.toString()).putInt("size", size);
}
public MarketDirection getMd() {
return md;
}
public UserOfferType getType() {
return type;
}
public boolean isTrade() {
return trade;
}
public int getPlast() {
return plast;
}
public double getCap() {
return cap;
}
public double getMult() {
return mult;
}
public int getSize() {
return size;
}
public void setTrade(boolean trade) {
this.trade = trade;
}
public void setPlast(int plast) {
this.plast = plast;
}
public void setCap(double cap) {
this.cap = cap;
}
public void setMult(double mult) {
this.mult = mult;
}
public void setSize(int size) {
this.size = size;
}
}
}
Since the only way for PropertyValueFactory to retrieve the value is using the getter, changes of a property cannot be observed and therefore the update only happens, when the item is associated with a new TableRow.
Starting with JavaFX 8u60 you can simply call the refresh method of TableView, which will force an update to be executed.
However the usual way of doing this is by providing access to a property object containing the property value, e.g.
In PreferencesSet
private final IntegerProperty plast = new SimpleIntegerProperty();
public void setPlast(int plast) {
this.plast.set(plast);
}
public int getPlast() {
return plast.get();
}
// this method will be used by the PropertyValueFactory
// and returns a Property which notifies TableView of changes
public IntegerProperty plastProperty() {
return plast;
}
There are other property types for the other data types, see javafx.beans.property package
I have a strange checkbox selection issue with nodes that have Children in a JavaFX 8 TreeView using CheckBoxTreeItems with custom CheckBoxTreeCell.
The Problem is that checkboxes of Nodes with children have to be clicked twice instead of once in order to be selected. Leaves require only a single click.
My CheckBoxTreeItems take Person Objects. I override the updateItem() Method in my CheckBoxTreeCells to set the value displayed to the name of the Person in the TreeCell. If I don't call setText() in my overidden updateItem Method, the TreeCell displays the default toString() Method of my Person object (which is not what I want) and all nodes behave a expected when selecting their checkboxes.
I do not want to change the default toString in class Person, so the only workaround I see is to write a Wrapper class for Person that returns the Persons name in its toString(). But I prefer resolving this issue properly rather than using a workaround!
Any ideas? Help would be much appreciated!
Here is the code I use:
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
and
public class TreeUtilTest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
VBox vBox = new VBox();
TreeView<Person> treeView = new TreeView<>();
treeView.setCellFactory(p -> new CheckBoxTreeCell<Person>() {
#Override
public void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item.getName());
}
}
});
vBox.getChildren().add(treeView);
CheckBoxTreeItem<Person> treeRoot = new CheckBoxTreeItem<>();
treeRoot.setValue(new Person("Peter", 10));
treeRoot.setIndependent(true);
treeView.setRoot(treeRoot);
IntStream.range(0, 10).forEach(i -> {
CheckBoxTreeItem<Person> item = new CheckBoxTreeItem<>();
item.setValue(new Person("Friend", i));
item.setIndependent(true);
treeRoot.getChildren().add(item);
});
Scene scene = new Scene(vBox);
primaryStage.setScene(scene);
primaryStage.show();
}
}
If you look at CheckBoxTreeCell class, you'll see it has the possibity to provide a callback that returns an ObservableValue<Boolean> with the selection status of the item, and a StringConverter.
So we can define these in our class:
final Callback<TreeItem<Person>, ObservableValue<Boolean>> getSelectedProperty =
(TreeItem<Person> item) -> {
if (item instanceof CheckBoxTreeItem<?>) {
return ((CheckBoxTreeItem<?>)item).selectedProperty();
}
return null;
};
final StringConverter<TreeItem<Person>> converter =
new StringConverter<TreeItem<Person>>() {
#Override
public String toString(TreeItem<Person> object) {
Person item=object.getValue();
return item.getName();
}
#Override
public TreeItem<Person> fromString(String string) {
throw new UnsupportedOperationException("Not supported yet.");
}
};
And now we just need to define the cell factory with these two parameters:
treeView.setCellFactory(p -> new CheckBoxTreeCell<>(getSelectedProperty,converter));
If you try this, you will see that you can select/unselect the root as well as the children with just one click.
EDIT
There's an easier solution, using the static method forTreeView, where you don't need to provide the callback nor the converter:
treeView.setCellFactory(CheckBoxTreeCell.<Person>forTreeView());
But for this to work you just need to override toString in Person:
private class Person {
...
#Override
public String toString() {
return name;
}
}
And this explains why you had the problem in the first place: if you add the toString method to your code, it will work also with your CheckBoxTreeCell:
treeView.setCellFactory(p -> new CheckBoxTreeCell<Person>() {
#Override
public void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item.getName());
}
}
});
I use two text fields and a button to add entries to a two column table.
If I add a new entry the table is updated right away:
private void addBtn(ActionEvent event) {
Test o = new Test();
o.setTitle(title.getText());
o.setCount(Integer.parseInt(count.getText()));
mainApp.getData().add(o);
}
In a second step I added an additional button to amend the highlighted count cell:
private void editBtn(ActionEvent event) {
Test o = getSelection();
o.setCount(Integer.parseInt(count.getText()));
mainApp.getData().set(tablePosition, o);
}
If I click the button, the cell will update the value, but it's not visible in the table. If I click the button a second time it will update the table.
To check for which row is highlighted I use the following functions:
private final ListChangeListener<Test> selector = new ListChangeListener<Test>() {
#Override
public void onChanged(ListChangeListener.Change<? extends Test> c) {
setSelection();
}
};
public Test getSelection() {
if (testTable != null) {
List<Test> table = testTable.getSelectionModel().getSelectedItems();
if (table.size() == 1) {
final Test selection = table.get(0);
return selection;
}
}
return null;
}
private void setSelection() {
final Test o = getSelection();
tablePosition = mainApp.getData().indexOf(o);
if (o != null) {
title.setText(o.getTitle());
count.setText(o.getCount().toString());
}
}
In the initialize method I add a listener to the observable list:
final ObservableList<Test> t1 = testTable.getSelectionModel().getSelectedItems();
t1.addListener(selector);
My Test class:
public class Test {
private final SimpleStringProperty title = new SimpleStringProperty();
private final SimpleIntegerProperty count = new SimpleIntegerProperty();
public void setTitle(String title) {
this.title.set(title);
}
public String getTitle() {
return title.get();
}
public void setCount(Integer count) {
this.count.set(count);
}
public Integer getCount() {
return count.get();
}
}
How can I make the Edit button to update the cell value right away?
Assuming you are using a PropertyValueFactory as the cell factory for your table columns, you need to provide property accessor methods in order that the table cell provided by the PropertyValueFactory can listen to those properties for changes.
One correct implementation of using the JavaFX Property model looks like
public class Test {
private final IntegerProperty count = new SimpleIntegerProperty(this, "count", 0);
private final StringProperty title = new SimpleStringProperty(this, "title", "");
public final int getCount() {
return count.get();
}
public final void setCount(int count) {
this.count.set(count);
}
public IntegerProperty countProperty() {
return count ;
}
public final String getTitle() {
return title.get();
}
public final void setTitle(String title) {
this.title.set(title);
}
public StringProperty titleProperty() {
return title ;
}
}
With that, the following method will then correctly update the selected row in the table:
private void editBtn(ActionEvent event) {
Test o = testTable.getSelectionModel().getSelectedItem();
if (o != null) {
o.setCount(Integer.parseInt(count.getText()));
}
}
If that doesn't fix the problem for you, I recommend you edit your question completely and provide a sscce that demonstrates the problem.
I have a datagrid in GWT, and I'm using RPC to populate it with data, I can get the data to show up just fine, and I can also select individual cells but when it comes to sorting it just doesn't work! I can occasionaly click on column headers (it happens intermittently and I'm not sure why) but when I do nothing sorts. I'm using a dataProvider, but I think I'm implementing it incorrectly, I've attached the related code, can someone give me a pointer on how to do this correctly?
first is the actual table itself
public class GuiInventory {
public final static LayoutPanel hpMain = new LayoutPanel();
static ListHandler<OpInventory> sortHandler;
/*
* Define a key provider for a Contact. We use the unique ID as the key,
* which allows to maintain selection even if the name changes.
*/
static ProvidesKey<OpInventory> keyProvider = new ProvidesKey<OpInventory>() {
#Override
public Object getKey(OpInventory item) {
// Always do a null check.
return (item == null) ? null : item.getPartID();
}
};
//the table
final static DataGrid<OpInventory> table = new DataGrid<OpInventory>(keyProvider);
final static SelectionModel<OpInventory> selectionModel = new MultiSelectionModel<OpInventory>(keyProvider);
/**
* The provider that holds the list of contacts in the database.
*/
private final static ListDataProvider<OpInventory> dataProvider = new ListDataProvider<OpInventory>();
public ListDataProvider<OpInventory> getDataProvider() {
return dataProvider;
}
/**
* Add a display to the database. The current range of interest of the display
* will be populated with data.
*
* #param display a {#Link HasData}.
*/
public void addDataDisplay(HasData<OpInventory> display) {
dataProvider.addDataDisplay(display);
}
/**
* Refresh all displays.
*/
public void refreshDisplays() {
dataProvider.refresh();
}
public static Widget init() {
hpMain.clear();
table.setWidth("100%");
table.setSelectionModel(selectionModel);
Ioma.dataservice.getPartInventory(new AsyncCallback<ArrayList<OpInventory>>() {
#Override
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
#Override
public void onSuccess(ArrayList<OpInventory> result) {
dataProvider.setList(result);
// Attach a column sort handler to the ListDataProvider to sort the list.
sortHandler = new ListHandler<OpInventory>(result);
table.addColumnSortHandler(sortHandler);
dataProvider.addDataDisplay(table);
if (table.getColumnCount() == 0) {
initTable();
}
}
});
//add in table
hpMain.add(table);
return hpMain;
}
public static void initTable() {
// Add a text column to show the part ID.
Column<OpInventory, Number> partIDColumn = new Column<OpInventory, Number>(new NumberCell()) {
#Override
public Integer getValue(OpInventory object) {
return object.getPartID();
}
};
table.addColumn(partIDColumn, "Part ID");
table.setColumnWidth(partIDColumn, 4, Unit.PX);
//add a sort to partID
partIDColumn.setSortable(true);
sortHandler.setComparator(partIDColumn, new Comparator<OpInventory>() {
#Override
public int compare(OpInventory o1, OpInventory o2) {
return Integer.valueOf(o1.getPartID()).compareTo(o2.getPartID());
}
});
// Add a text column to show the part Number.
Column<OpInventory, String> partNumberColumn = new Column<OpInventory, String>(new EditTextCell()) {
#Override
public String getValue(OpInventory object) {
return object.getPartNumber();
}
};
table.addColumn(partNumberColumn, "Part Number");
table.setColumnWidth(partNumberColumn, 4, Unit.PX);
//add a sort to the part Number
partNumberColumn.setSortable(true);
sortHandler.setComparator(partNumberColumn, new Comparator<OpInventory>() {
#Override
public int compare(OpInventory o1, OpInventory o2) {
return o1.getPartNumber().compareTo(o2.getPartNumber());
}
});
//add a field updater to be notified when the user enters a new Part Number
partNumberColumn.setFieldUpdater(new FieldUpdater<OpInventory, String>() {
#Override
public void update(int index, OpInventory object, String value) {
object.setPartNumber(value);
//TODO add async call to database to update part Number
table.redraw();
}
});
// Add a text column to show the name.
Column<OpInventory, String> nameColumn = new Column<OpInventory, String>(new EditTextCell()) {
#Override
public String getValue(OpInventory object) {
return object.getName();
}
};
table.addColumn(nameColumn, "Name");
table.setColumnWidth(nameColumn, 10, Unit.PX);
//add a field updater to be notified when the user enters a new part name
nameColumn.setFieldUpdater(new FieldUpdater<OpInventory, String>() {
#Override
public void update(int index, OpInventory object, String value) {
object.setName(value);
//TODO add async call to database to update part name
table.redraw();
}
});
//add a sort to the name
nameColumn.setSortable(true);
sortHandler.setComparator(nameColumn, new Comparator<OpInventory>() {
#Override
public int compare(OpInventory o1, OpInventory o2) {
return o1.getName().compareTo(o2.getName());
}
});
}
this is the Opinventory class to hold each object in the datagrid
public class OpInventory implements Comparable<OpInventory>, IsSerializable {
int partID;
String partNumber;
String name;
String desc;
String partLotNumber;
String supplier;
String reOrderNumber;
boolean isActive;
int quantity;
Double price;
/**
* The key provider that provides the unique ID of a contact.
*/
public static final ProvidesKey<OpInventory> KEY_PROVIDER = new ProvidesKey<OpInventory>() {
#Override
public Object getKey(OpInventory item) {
return item == null ? null : item.getPartID();
}
};
#Override
public int compareTo(OpInventory o) {
return (o == null || o.partNumber == null) ? -1 : -o.partNumber.compareTo(partNumber);
}
#Override
public boolean equals(Object o) {
if (o instanceof OpInventory) {
return partID == ((OpInventory) o).partID;
}
return false;
}
#Override
public int hashCode() {
return partID;
}
public OpInventory(int partID, String partNumber, String name, String desc, String partLotNumber, String supplier, String reOrderNumber, Double price, boolean isActive) {
this.partID = partID;
this.partNumber = partNumber;
this.name = name;
this.desc = desc;
this.partLotNumber = partLotNumber;
this.supplier = supplier;
this.reOrderNumber = reOrderNumber;
this.price = price;
this.isActive = isActive;
}
public OpInventory() {
}
//getters and setters here
}
Apparently my issue was with the fact that I had a keyProvider in both classes, instead of just one. I removed it from the OpInventory class and it seems to work now. this looks like a very specific issue so I expect this question will be closed soon. but I'll leave the code there for future analysis.