JavaFX 8 TreeView showing CheckBoxTreeItem with custom CheckBoxTreeCell - Checkbox Selection issue - java

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());
}
}
});

Related

Implementing an Iterator on Composite Design Pattern Through Separate Class - Is It programmatically valid?

I have implemented the Composite Design Pattern and then expanded the Composite class to also implement Iterable, however the iterator() method (which returns an iterator object) is also part of the abstract Component class and is then implemented by the Composite class (but not the Leaf class).
I want to implement a depth first and breadth first search for a tree-like structure. See summarized code below:
public abstract class Component {
public void add() {
}
public void remove() {
}
public ArrayList<Component> getItems() {
}
public ItemIterator iterator() {
}
public class Composite extends Component implements Iterable<Component> {
ArrayList<Component> items = new ArrayList<Component>();
String name;
public ItemIterator iterator() {
return new ItemIterator(this);
}
public Composite(String name) {
this.name = name;
}
public getName() {
// returns name
}
public ArrayList<Component> getItems() {
return this.items;
}
public class ItemIterator implements Iterator<Component> {
ArrayList<Component> breadthFirstSearch = new ArrayList<Component>();
Component currentItem;
public ItemIterator(Component firstItem) {
currentItem = firstItem;
breadthFirstSearch.add(currentItem);
}
public boolean hasNext() {
if (breadthFirstSearch.isEmpty()) {
return false;
}
return true;
}
public Component next() {
// This method pops the root item the first time, creates its children,
// places at end of ArrayList,
// then returns the root. Second time the same operations are performed
// on the following item in the breadth first traversal of the tree.
if (hasNext()) {
Component nextItem = breadthFirstSearch.get(0);
if (nextItem instanceof Composite) {
for (Component item : currentItem.getItems()) {
breadthFirstSearch.add(item);
}
}
breadthFirstSearch.remove(0);
if (hasNext()) {
currentItem = breadthFirstSearch.get(0);
}
return nextItem;
}
return null;
}
public class Demo {
public static void main(String[] args) {
Component bag = new Composite("bag");
Component plasticBag = new Composite("plastic bag");
Component makeupBag = new Composite("makeup bag");
Component phone = new Composite("phone");
Component lipstick = new Composite("lipstick");
Component mascara = new Composite("mascara");
bag.add(plasticBag); bag.add(makeupBag);
plasticbag.add(phone); makeupBag.add(lipstick); makeupBag.add(mascara);
ItemIterator itr = bag.iterator();
while (itr.hasNext()) {
System.out.println(itr.next().getName());
}
}
}
The code above compiles and runs fine, it works. However, I am not certain of whether it is programmatically acceptable. The structure of it seems to fundamentally go against other Iterator implementations that I have seen (implementations that I discovered after finishing the above solution), but I can't quite grasp/explain what is so wrong about it. The other way of implementing Iterable (in a different context) was of the form:
public abstract class Component {
public void add() {
}
public void remove() {
}
public ArrayList<Component> getItems() {
}
}
Note the lack of an iterator() method in the abstract class above.
public class Composite extends Component implements Iterable<Component> {
ArrayList<Component> items = new ArrayList<Component>();
String name;
public Iterator<Component> iterator() {
return new Iterator() {
public boolean hasNext() {
// Code
}
public Iterator<Component> next() {
// Code
};
}
public Composite(String name) {
this.name = name;
}
public getName() {
// returns name
}
public ArrayList<Component> getItems() {
return this.items;
}
}
Which way of structuring the solution is better, and is my way of doing it outright wrong/bad practice and if so, why? I am new to Java, so I apologize if this turns out to be a bad question.
I think you described the visitor pattern:
interface Visitable {
void accept(Visitor v);
}
class Visitor {
void visit(Component c){
c.doFooBar();// implement your logic here
}
}
class Component implements Visitable {
private List<Component> children;
void accept(Visitor v){
v.visit(this);
children.forEach(child -> child.accept(v)); // sumbit the visitor/iterator down the composite tree
}
}
public static void main(String[] args){
Component composite = Factory.createComposite();
composite.accept(new Visitor());
}
Instead of having iterator build up a list of pending items to iterate, it should just store a list of pending iterators to traverse.
Here is a Minimal, Reproducible Example:
public final class Node {
private final String name;
private List<Node> children = new ArrayList<>();
public Node(String name) {
this.name = name;
}
public Node(String name, Node... children) {
this.name = name;
this.children.addAll(Arrays.asList(children));
}
public String getName() {
return this.name;
}
public List<Node> getChildren() {
return this.children;
}
public Iterable<Node> breadthFirstSearch() {
return () -> new NodeIterator(this, true);
}
public Iterable<Node> depthFirstSearch() {
return () -> new NodeIterator(this, false);
}
#Override
public String toString() {
return "Node[" + this.name + "]";
}
}
public final class NodeIterator implements Iterator<Node> {
private final Deque<Iterator<Node>> iterators = new ArrayDeque<>();
private final boolean breadthFirst;
public NodeIterator(Node node, boolean breadthFirst) {
this.iterators.add(Collections.singleton(node).iterator());
this.breadthFirst = breadthFirst;
}
#Override
public boolean hasNext() {
return ! this.iterators.isEmpty();
}
#Override
public Node next() {
Iterator<Node> iterator = this.iterators.removeFirst();
Node node = iterator.next();
if (iterator.hasNext())
this.iterators.addFirst(iterator);
if (! node.getChildren().isEmpty()) {
if (this.breadthFirst)
this.iterators.addLast(node.getChildren().iterator());
else
this.iterators.addFirst(node.getChildren().iterator());
}
return node;
}
}
Test
Node root = new Node("root",
new Node("1",
new Node("1.1",
new Node("1.1.1"),
new Node("1.1.2")),
new Node("1.2",
new Node("1.2.1"),
new Node("1.2.2"))
),
new Node("2",
new Node("2.1",
new Node("2.1.1"),
new Node("2.1.2")),
new Node("2.2",
new Node("2.2.1"),
new Node("2.2.2"))));
for (Node node : root.breadthFirstSearch())
System.out.println(node);
System.out.println();
for (Node node : root.depthFirstSearch())
System.out.println(node);
Output
Node[root]
Node[1]
Node[2]
Node[1.1]
Node[1.2]
Node[2.1]
Node[2.2]
Node[1.1.1]
Node[1.1.2]
Node[1.2.1]
Node[1.2.2]
Node[2.1.1]
Node[2.1.2]
Node[2.2.1]
Node[2.2.2]
Node[root]
Node[1]
Node[1.1]
Node[1.1.1]
Node[1.1.2]
Node[1.2]
Node[1.2.1]
Node[1.2.2]
Node[2]
Node[2.1]
Node[2.1.1]
Node[2.1.2]
Node[2.2]
Node[2.2.1]
Node[2.2.2]

Inserting Hyperlink in JavaFx TableView

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.

Java programming error Pass by reference

I have an java application where a object reference "Validate.Options" is passed as parameter to the function "ValidateResult(Validate.Options option)" and the function is called iterative. Within this function based on the certain condition the property "enableProcessing" of the passed object gets changed which does not get reset on the next iterate. How can I reset this property?
Below is the sample code.
public interface Validate
{
public List validate();
public class Options implements Serializable
{
public String name;
public boolean enableProcessing = true;
public Options(String name)
{
this.name = name;
}
}
}
public class Coder
{
public String name;
public int age;
public Coder(String name, int age)
{
this.name = name;
this.age = age;
}
public void ValidateResult(Validate.Options option)
{
if(option.name.equals(this.name) && option.enableProcessing)
{
option.enableProcessing = false;
//
//business logic and function call
//
}
}
public static void main(String[] args)
{
Validate.Options options = new Validate.Options("Test");
List<Coder> coders = new ArrayList<Coder>();
Coder coder = new Coder("Test", 28);
Coder coder1 = new Coder("XYZ", 18);
Coder coder2 = new Coder("Test", 16);
coders.add(coder);
coders.add(coder1);
coders.add(coder2);
for(Coder co : coders)
{
co.ValidateResult(options);
}
}
}
If I understood the question well - in your for loop, simply add a line of code to reset the value of your public Validate.Options.enableProcessing field
for(Coder co : coders)
{
//reset options object for the next iteration
options.enableProcessing = true;
co.ValidateResult(options);
}
Make options immutable if you do not want it to be changed:
public class Options implements Serializable
{
public final String name; // final prevents changes
public final boolean enableProcessing = true; // final prevents changes
public Options(String name)
{
this.name = name;
}
}
To locally work with enableProcessing copy its value to a local variable.
public void ValidateResult(Validate.Options option)
{
boolean enableProcessing = option.enableProcessing; // create local copy
if(option.name.equals(this.name) && enableProcessing) // use local copy
{
enableProcessing = false; // only change local copy
//
//business logic and function call
//
}
}
Alternatively create new, fresh Options for each loop:
public static void main(String[] args)
{
List<Coder> coders = Arrays. asList(
new Coder("Test", 28),
new Coder("XYZ", 18),
new Coder("Test", 16)
);
for(Coder co : coders)
{
Validate.Options options = new Validate.Options("Test"); // fresh options for each iteration
co.ValidateResult(options);
}
}

Update highlighted cell in javafx

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.

sorting a datagrid with GWT

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.

Categories

Resources