I'm writing a seating chart program using JavaFX. I have a table that keeps a list of students together that holds their name, grade, and whether they are present or absent (using a checkbox). I have a delete button that allows me to delete the students from the list. This works fine, however, whenever I delete the student object, the checkbox does not go along with it. I'm not sure what I would need to add to get that to work. Here is a snippet of the delete code. There are also two images below that show my problem. This is my first post so please let me know if I missed something. Please help! Thanks!
ObservableList<Student> items, sel;
items = currentTable.getItems();
sel = currentTable.getSelectionModel().getSelectedItems();
Student s = new Student("", "", 0, "");
for (Student p : sel) {
items.remove(p);
s = p;
}
Before Delete
After Delete
This has nothing to do with the delete or remove method. It has to do with what you did in TableColumn.setCellFactory().
To get the checkbox you shown, you should have used (in general) one of the two methods:
Overriding updateItem() in TableCell while setting Cell Factory
There is this empty parameter in updateItem() which indicates whether the row is empty or not. You need to use that to determine when not to show the checkbox.
column.setCellFactory(col -> {
return new TableCell<Foo, Boolean>() {
final CheckBox checkBox = new CheckBox();
#Override
public void updateItem(final Boolean selected, final boolean empty) {
super.updateItem(selected, empty);
if (!this.isEmpty()) {
setGraphic(checkBox);
setText("");
}
else {
setGraphic(null); // Remove checkbox if row is empty
setText("");
}
}
}
}
Using CheckBoxTableCell
JavaFX API has this convenient class CheckBoxTableCell that would do all these for you. Most people find this class hard to use because there are 2 things that you need to ensure to use it correctly:
The TableView that the column belongs to must be editable.
The TableColumn itself must be editable.
Example:
tableView.setEditable(true);
tableColumnSelected.setCellFactory(CheckBoxTableCell.forTableColumn(tableColumnSelected));
tableColumnSelected.setEditable(true);
As for whether which entry you want to be removed with the delete button, you just need to remove the correct items from the TableView.
Related
I am using cell factory for listview with checkboxes like:
listView.setCellFactory(CheckBoxListCell.forListView(new Callback < Bean, ObservableValue < Boolean >> () {
#Override
public ObservableValue < Boolean > call(Bean item) {
BooleanProperty observable = new SimpleBooleanProperty();
observable.addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
if (!beanChoices.contains(item.toString())) {
beanChoices.add(item.toString());
observable.setValue(true);
//listView.scrollTo(listView.getItems().size() - 1);
}
} else if (wasSelected) {
if (beanChoices.contains(item.toString())) {
beanChoices.remove(item.toString());
observable.setValue(false);
}
}
});
/* [Code] which compares values with bean item string value and select observable to true for that for edit mode
but here the observer not called for beanItem that are under scrollpane of listview. But on scroll it gets called. */
return observable;
}
}));
It works fine but not for all cases.
Case: When I have say more than 10 entries, the scrollpane comes. Say I have beanChoices to be checked that are at 8 or 9 index(you have to scroll to view them). The listener is not called for the items not visible(that are under scrollpane). On Debug, I found that listener is called when I scroll down.
Problem: when I get checked values from beanChoices for above case, it return empty.
Detail: I have beanChoices which I need to make checked for listview items (edit mode). When I update without changing anything. (Assume that the value which is under the scrollpane of listview will be selected and added to beanChoices)
The Callback is used to retrieve the property for the checked state when the item is associated with a cell. The item may be removed from a cell and put in a new one at any time. This is how ListView (and similar controls like TableView) works. CheckBoxListCell simply gets the checked state property every time a new item is associated with the cell.
The return value is also used to set the initial state of the CheckBox. Since you do not properly initialize the property with the correct value the initial state is not preserved.
Also note that it makes little sense to update the value of the property to the new value in the change listener. It happens anyway.
Since BooleanProperty is a wrapper for primitive boolean the possible values are true and false; the ChangeListener only gets called when !Objects.equals(oldValue, newValue) you can be sure that isNowSelected = !wasSelected.
Of course you also need to return the value:
#Override
public ObservableValue < Boolean > call(Bean item) {
final String value = item.toString();
BooleanProperty observable = new SimpleBooleanProperty(beanChoices.contains(value));
observable.addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
beanChoices.add(value);
} else {
beanChoices.remove(value);
}
});
return observable;
}
I also recommend using a Collection of Beans instead of relying on the string representation of the objects. toString many not produce unique results and Beans.equals would be the better choice to compare the objects.
Is there a way to check if a ComboBox has any items in it or whether it is empty? I have an array of ComboBoxes and I need to go through each of them, if there are no items in the ComboBox, then I must hide it. This following code doesn't seem to work:
for (ComboBox cmb : comboBoxes) {
if (cmb.getItems().isEmpty()) {
cmb.hide();
}
}
The code for checking, if the ComboBox has no items is correct, you code for hiding the ComboBoxes is incorrect however.
ComboBox.hide only closes the popup showing the items, if it's open. It does not hide the ComboBox. To hide the ComboBox, you need to set the visibility:
for (ComboBox cmb : comboBoxes) {
if (cmb.getItems().isEmpty()) {
cmb.setVisible(false);
}
}
Alternatively to call a method to hide the ComboBoxes, you can bind the visibleProperty of the ComboBoxes to their own itemsProperty with a custom binding:
List<ComboBox<String>> comboBoxes = new ArrayList<>();
for(int i = 0; i< 10; i++) {
ComboBox<String> combo = new ComboBox<>();
combo.visibleProperty().bind(Bindings.createBooleanBinding(() -> !combo.getItems().isEmpty(),
combo.itemsProperty().get()));
comboBoxes.add(combo);
}
The advantage is, that you don't have to call any methods to hide your ComboBoxes, because the binding is evaluated automatically, therefore no one can see your empty combos.
The .getItems() method returns an ObservableList<T> so you can just check its .size(). This will tell you if it's empty.
for (ComboBox cmb : comboBoxes) {
if (cmb.getItems().size() <= 0) { // or cmb.getItems().isEmpty()
cmb.setVisible(false); }
}
If the ComboBox is populated by a List of its own, you could also just check if the list is empty with the same .size() call.
I have customized ListGrid, in which records can be expanded and expansion component is displayed. I know there exists little arrow to expand/collapse record in upper left corner of the record, but I wonder if it is possible to manually check if the selected record is expanded or collapsed. I want the record to expand/collapse on single record click. My code example:
private RecordClickHandler gatherRecordClickHandler() {
return new RecordClickHandler() {
#Override
public void onRecordClick(RecordClickEvent event) {
//Here i want to check if the record is expanded/collapsed
if(/*expanded check method here*/)
collapseRecord(event.getRecord());
else
expandRecord(event.getRecord());
}
};
}
Try this:
if(myListGrid.isExpanded(event.getRecord))
collapseRecord(event.getRecord());
else
expandRecord(event.getRecord());
I am using this link for creating a ContextMenu for each table row. Right now I'm running into problems because I'm not sure how to attach a ContextMenu after the 'type' has been inserted into a row.
Lets say I'm using a .zip editor program, and it lists the contents. I have an Image, and a text file, and some other stuff, all of them are under a class called Entry. My table's generic type is 'Entry', and I'd like to be able to create a context menu for each entry based on it's underlying subclass type (like an ImageEntry might return a menu item to open it up in an image editor...etc).
Right now I have a generic context menu for everything, but it's not great displaying a menu item about opening a text file with an image editor...
Is this possible to do? If so, what is the proper way to go about doing it?
Add a listener to the row's itemProperty (which represents the item displayed in the row) and update the context menu when it changes:
table.setRowFactory(new Callback<TableView<Person>, TableRow<Person>>() {
#Override
public TableRow<Person> call(TableView<Person> tableView) {
final TableRow<Person> row = new TableRow<>();
final ContextMenu contextMenu = new ContextMenu();
row.itemProperty().addListener((obs, oldPerson, newPerson) -> {
contextMenu.getItems().clear();
// add items to context menu depending on value of newPerson
// ...
});
// Set context menu on row, but use a binding to make it only show for non-empty rows:
row.contextMenuProperty().bind(
Bindings.when(row.emptyProperty())
.then((ContextMenu)null)
.otherwise(contextMenu)
);
return row ;
}
});
I want to completely view updates when a row is selected in a CellTable. How can this be done? In the following test case, using NoSelectionModel, the view is still updated: clicking on a row changes the background and border colors of the row until another row is clicked.
CellTable<String> table = new CellTable<String>();
TextColumn<String> column = new TextColumn<String>()
{
#Override
public String getValue(String string)
{
return string;
}
};
table.addColumn(column);
List<String> sampleData = Arrays.asList("foo", "bar", "baz");
table.setRowData(sampleData);
final NoSelectionModel<String> selectionModel = new NoSelectionModel<String>();
table.setSelectionModel(selectionModel);
RootPanel.get().add(table);
I've also attempted to subclass SingleSelectionModel with empty override methods, without success.
I can fake the behavior I want by providing empty CSS stylings for selected rows, but that method seems hack-ish.
What you're seeing is the keyboard-selection highlighting (really useful when you're not using the mouse to interact with the table).
You can disable it using setKeyboardSelectionPolicy(KeyboardSelectionPolicy.DISABLED)