I want to add checkBox to tableCell but i dont know what to do to make checkBox visible when i use .setCellFactory
I want to achive this with cellFactory function ---> checkBoxVisible
when i want to make use of cellFactory checkBox are not visible
----> wrong effect
window1.setCellFactory(new BooleanColorCellFactory());
window2.setCellFactory(new BooleanColorCellFactory());
This is BooleanColorCellFactory Class
#Override
public TableCell<Server, CheckBox> call(TableColumn<Server, CheckBox> param) {
return new TableCell<Server, CheckBox>(){
#Override
protected void updateItem(CheckBox item, boolean empty) {
super.updateItem(item, empty);
// if(!empty) {
// setVisible(true);
// setEditable(true);
// getChildren().add(item);
// setText(item.toString());
// if(item.isSelected())
// setStyle(" -fx-background-color: Green");
// else
// setStyle(" -fx-background-color: RED");
}
// }
};
}
}
I have tried some things but nothing was working.
What i need to add in BooleanColorCellFactory to make this work ?
UPDATE:
So i was playing around and i manage to get step closer to solution by adding this into BooleanColorCellFactory Class
if(!getChildren().contains(item))
getChildren().add(item);
but it is buggy and dont looks well and its added after i start scrolling(which is weird behavior for me)--> click
You shouldn't put a Node inside the item class unless you really need to. Furthermore never access the children of a Control directly unless you're writing a Skin for this Control.
You should instead add a BooleanProperty to the Server class:
private final BooleanProperty window1 = new SimpleBooleanProperty();
public boolean isWindow1() {
return window1.get();
}
public void setWindow1(boolean value) {
window1.set(value);
}
public BooleanProperty window1Property() {
return window1;
}
TableColumn<Server, Boolean> window1;
Callback<TableColumn<Server, Boolean>, TableCell<Server, Boolean>> factory = new BooleanColorCellFactory();
// cellValueFactory returns property
window1.setCellValueFactory(new PropertyValueFactory<>("window1"));
window1.setCellFactory(factory);
...
window2.setCellFactory(factory);
#Override
public TableCell<Server, Boolean> call(TableColumn<Server, Boolean> param) {
return new TableCell<Server, Boolean>(){
private final CheckBox checkBox = new CheckBox();
{
checkBox.selectedProperty().addListener((o, oldValue, newValue) -> {
// save new value in row item
WritableValue<Boolean> property = (WritableValue<Boolean>) getTableColumn().getCellObservableValue​(getIndex());
property.setValue(newValue);
});
}
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
setStyle(null);
} else {
setGraphic(checkBox);
checkBox.setSelected(item);
setStyle(item ? "-fx-background-color: Green" : "-fx-background-color: RED");
}
}
};
}
Procede accordingly for window2
Related
I have a TableView with an editable TextFieldTableCell that I want to restrict to be available based on a BooleanProperty of my model object.
For example, textField.disableProperty().bind(item.editableProperty().not())
Currently, I have the basic implementation from the Oracle docs:
colComment.setCellFactory(TextFieldTableCell.forTableColumn());
colComment.setOnEditCommit(event -> event.getTableView().getItems().get(
event.getTablePosition().getRow()).setComment(
event.getNewValue())
);
This obviously does not allow much flexibility. The desire is to check the item's editableProperty and if it is true, display the TextFieldTableCell and bind it to the item's commentProperty.
If that property is false, the cell should simply display the value of the commentProperty.
I have not worked with editable TableViews in the past so I am a bit lost.
I have tried to hack out a workaround with manually setting the graphic myself, but that just does nothing with the cell:
colComment.setCellFactory(cell -> new TableCell<LogEntry, String>() {
final TextField txtComment = new TextField();
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
} else {
LogEntry logEntry = (LogEntry) getTableRow().getItem();
if (logEntry.isEditable()) {
txtComment.textProperty().bindBidirectional(logEntry.commentProperty());
setGraphic(txtComment);
} else {
setText(item);
}
}
}
});
The basic approach is to disallow cell's editing based on a condition. TextFieldTableCell has no direct support for such, but can be extended just as any other type of cell. Options are
override startEdit to do nothing if the condition is not met
bind the cell's editability property to a condition of the rowItem
The most simple is the first (the latter is a bit more involved, due to requiring updates when parent TableRow and its item changes). A quick example (all boiler-plate except the cell ;):
public class TableCellConditionalEditable extends Application {
/**
* Cell with custom condition to prevent editing.
*/
public static class ConditionalEditableCell extends TextFieldTableCell<ConditionalWritable, String> {
public ConditionalEditableCell() {
super(new DefaultStringConverter());
}
/**
* Overridden to do nothing if rowItem-related condition not met.
*/
#Override
public void startEdit() {
if (!isConditionalEditable()) return;
super.startEdit();
}
private boolean isConditionalEditable() {
if (getTableRow() == null || getTableRow().getItem() == null || isEmpty()) return false;
return getTableRow().getItem().writableProperty().get();
}
}
private Parent createContent() {
TableView<ConditionalWritable> table = new TableView<>(ConditionalWritable.conditionalWritables());
TableColumn<ConditionalWritable, String> text = new TableColumn<>("Text");
text.setCellValueFactory(cc -> cc.getValue().textProperty());
TableColumn<ConditionalWritable, Boolean> writable = new TableColumn<>("Writable");
writable.setCellValueFactory(cc -> cc.getValue().writableProperty());
table.getColumns().addAll(text, writable);
table.setEditable(true);
text.setCellFactory(cc -> new ConditionalEditableCell());
BorderPane content = new BorderPane(table);
return content;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
public static class ConditionalWritable {
private SimpleStringProperty text;
private SimpleBooleanProperty writable;
public ConditionalWritable(String text, boolean writable) {
this.text = new SimpleStringProperty(text);
this.writable = new SimpleBooleanProperty(writable);
}
public StringProperty textProperty() {
return text;
}
public BooleanProperty writableProperty() {
return writable;
}
public static ObservableList<ConditionalWritable> conditionalWritables() {
return FXCollections.observableArrayList(
new ConditionalWritable("some data", false),
new ConditionalWritable("other data", true),
new ConditionalWritable("nothing important", true)
);
}
}
}
how to do the Check-Box enable disable in a Table-Column In JavaFX if i select one check-box i need to disable the remaining check-Boxes .
if i select one check-box i need to disable the remaining check-Boxes in the table view, in JavaFx.
colSelect.setCellFactory(new Callback<TableColumn<LogVoiceBroadCast, Boolean>, TableCell<LogVoiceBroadCast, Boolean>>()
{
#Override
public TableCell<LogVoiceBroadCast, Boolean> call(TableColumn<LogVoiceBroadCast, Boolean> param) {
final TableCell<LogVoiceBroadCast, Boolean> cell = new TableCell<LogVoiceBroadCast, Boolean>() {
#Override
public void updateItem(Boolean value, boolean empty) {
super.updateItem(value, empty);
if (!empty || value != null) {
CheckBox checkBox = new CheckBox();
checkBox.setSelected(false);
checkBox.addEventHandler(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (!checkBox.isSelected()) {
LogVoiceBroadCast item = (LogVoiceBroadCast) getTableRow().getItem();
voiceFiles.add(item);
} else {
LogVoiceBroadCast item = (LogVoiceBroadCast) getTableRow().getItem();
voiceFiles.remove(item);
}
}
});
final HBox hbox = new HBox(checkBox);
hbox.setAlignment(Pos.CENTER);
hbox.setSpacing(12);
setGraphic(hbox);
} else {
setGraphic(null);
}
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
You shouldn't use a for input events to trigger listen to changes in the CheckBox state. Instead add a listener to the CheckBox.selected property.
Furthermore you need to implement a way of restoring the CheckBox state, since different TableCells may be used for the "checked row". Use some observable data structure to store this kind of information and use a listener to update the state of the CheckBoxes:
TableView<Integer> table = new TableView<>();
for (int i = 0; i < 100; i++) {
table.getItems().add(i);
}
TableColumn<Integer, Number> column = new TableColumn<>();
column.setCellValueFactory(cd -> new SimpleIntegerProperty(cd.getValue()));
// property to store the checked row item or null, if no row is checked
final ObjectProperty<Integer> selectedValue = new SimpleObjectProperty<>();
TableColumn<Integer, Void> checkBoxColumn = new TableColumn<>();
checkBoxColumn.setCellFactory(col -> new TableCell<Integer, Void>() {
private final CheckBox checkBox = new CheckBox();
private final ChangeListener<Boolean> selectionListener = (o, oldValue, newValue) -> {
if (newValue) {
// use the row item as selected item, when checkbox is selected
selectedValue.set((Integer) getTableRow().getItem());
} else {
// clear selected item, when checkbox is unselected
selectedValue.set(null);
}
};
private final ChangeListener<Integer> listener = (o, oldValue, newValue) -> {
if (!isEmpty()) {
// update state without triggering the listener
checkBox.selectedProperty().removeListener(selectionListener);
if (newValue == null) {
checkBox.setSelected(false);
checkBox.setDisable(false);
} else {
boolean match = newValue.equals(getTableView().getItems().get(getIndex()));
checkBox.setSelected(match);
checkBox.setDisable(!match);
}
checkBox.selectedProperty().addListener(selectionListener);
}
};
{
// listener should not prevent garbage collection of cell
selectedValue.addListener(new WeakChangeListener<>(listener));
checkBox.selectedProperty().addListener(selectionListener);
setAlignment(Pos.CENTER);
}
#Override
protected void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(checkBox);
listener.changed(null, null, selectedValue.get());
}
}
});
table.getColumns().addAll(column, checkBoxColumn);
As the title states, I'm trying to enable/disable a button within a table row based upon a boolean within that table row's data. Here's my code so far:
col.setCellFactory(new Callback<TableColumn<ExampleRow, String>, TableCell<ExampleRow, String>>() {
#Override
public TableCell call(final TableColumn<ExampleRow, String> param){
final Button btn = new Button("Save");
final TableCell<ExampleRow, String> cell = new TableCell<ExampleRow, String>(){
#Override
public void updateItem(String item, boolean empty){
super.updateItem(item, empty);
if(empty){
setGraphic(null);
setText(null);
} else {
btn.setPrefWidth(col.getWidth());
btn.setPadding(Insets.EMPTY);
btn.setOnAction(event -> {
});
setGraphic(btn);
setText(null);
}
}
};
ExampleRow row = (ExampleRow)cell.getTableRow().getItem(); //NPE here
btn.setDisable(!row.hasChanged());
return cell;
}
});
Unfortunately my code breaks on the fifth from the bottom line. If I exclude that line and change the line below to btn.setDisable(true) it works wonderfully. What can I do to disable this button based upon the data in which the button resides?
You aren't using the item anyways, so you could just make it a Boolean and use the value of the changed property. This allows you to enable/disable the button in the updateItem method:
Example:
public static class Item {
private final BooleanProperty changed = new SimpleBooleanProperty();
public final boolean isChanged() {
return this.changed.get();
}
public final void setChanged(boolean value) {
this.changed.set(value);
}
public final BooleanProperty changedProperty() {
return this.changed;
}
}
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView();
table.getItems().addAll(new Item(), new Item(), new Item());
TableColumn<Item, Boolean> column = new TableColumn<>();
column.setCellValueFactory(cd -> cd.getValue().changedProperty());
column.setCellFactory(col -> new TableCell<Item, Boolean>() {
final Button btn = new Button("Save");
{
btn.setOnAction(evt -> {
Item item = (Item) getTableRow().getItem();
item.setChanged(false);
});
}
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
btn.setDisable(!item);
setGraphic(btn);
}
}
});
table.getColumns().add(column);
Button btn = new Button("change");
btn.setOnAction((ActionEvent event) -> {
Item item = table.getSelectionModel().getSelectedItem();
if (item != null) {
item.setChanged(true);
}
});
VBox root = new VBox(btn, table);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
BTW: TableView uses the cellFactory to create the cells. The item, table and tableRow properties are updated later. Therefore retrieving any of those values in the cellFactory's call method itself makes no sense, since none of those values have been assigned at that time.
I'm applying the below cell factory to a column.
targetEnviroment.setCellFactory(new Callback<TableColumn<DevWorkTabBench, String>, TableCell<DevWorkTabBench, String>>() {
#Override
public TableCell<DevWorkTabBench, String> call(TableColumn<DevWorkTabBench, String> param) {
TableCell<DevWorkTabBench, String> cell = new TableCell<DevWorkTabBench, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
String status = null;
try {
status = getTableView().getItems().get(getIndex()).getObjectStatus();
} catch (IndexOutOfBoundsException ex) {
status = "";
}
if (status.equalsIgnoreCase("ReadyForDeployment")) {
ComboBox<String> comboBox = new ComboBox(environmentList);
comboBox.valueProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
commitEdit(newValue);
}
});
comboBox.setOnShown(new EventHandler<Event>() {
#Override
public void handle(Event event) {
getTableView().edit(getIndex(), getTableColumn());
getTableView().getSelectionModel().select(getIndex());
}
});
comboBox.setValue(item);
setGraphic(comboBox);
} else {
setGraphic(null);
}
if (empty) {
setGraphic(null);
}
}
};
return cell;
}
});
When I change the status to the mentioned status, I get the look of ComboBox in that particular cell but the drop down does not occur. Even after multiple clicks no action seems to be performed on the combobox. I do not get any exception other than the handled one. Other columns are editable and performing task as expected.
I have no idea what is wrong here. Can anyone please help me.
Since you are always displaying the combo box in the (non-empty) cells, you don't really need to go into "editing" mode as the standard TextFieldTableCell etc does. Your implementation is more similar to the CheckBoxTableCell, which essentially bypasses the editing mechanism. From the documentation for that class:
Note that the CheckBoxTableCell renders the CheckBox 'live', meaning
that the CheckBox is always interactive and can be directly toggled by
the user. This means that it is not necessary that the cell enter its
editing state (usually by the user double-clicking on the cell). A
side-effect of this is that the usual editing callbacks (such as on
edit commit) will not be called. If you want to be notified of
changes, it is recommended to directly observe the boolean properties
that are manipulated by the CheckBox.
So your cell implementation behaves similarly: don't invoke edit(...) (which I think is messing things up) and don't rely on the commitEdit(...), cancelEdit() etc methods (which won't work as you're not in editing state), but just update the model class directly.
I can't test, since there isn't a MCVE to work from, so this might not work directly, but it should be enough to get you started toward something that will work.
targetEnviroment.setCellFactory(new Callback<TableColumn<DevWorkTabBench, String>, TableCell<DevWorkTabBench, String>>() {
#Override
public TableCell<DevWorkTabBench, String> call(TableColumn<DevWorkTabBench, String> param) {
TableCell<DevWorkTabBench, String> cell = new TableCell<DevWorkTabBench, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null) ;
} else {
String status = getTableView().getItems().get(getIndex()).getObjectStatus();
if (status.equalsIgnoreCase("ReadyForDeployment")) {
ComboBox<String> comboBox = new ComboBox(environmentList);
comboBox.valueProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
//commitEdit(newValue);
getTableView().getItems().get(getIndex()).setObjectStatus(newValue);
}
});
comboBox.setValue(item);
setGraphic(comboBox);
} else {
setGraphic(null);
}
}
}
};
return cell;
}
});
I have a treetableview with 4 level hierarchy. I want to have a column with cell factory as combobox. but this combobox should be visible only at the last child level and not the parent level. Is that possible to achieve?
Edit: I tried the following as per suggestions received.
ObservableList ADM = WSData.getADMObjectMapList();
tbAdmMap.setCellFactory(new Callback<TreeTableColumn<ProjectPlan, String>, TreeTableCell<ProjectPlan, String>>() {
#Override
public TreeTableCell<ProjectPlan, String> call(TreeTableColumn<ProjectPlan, String> param) {
TreeTableCell<ProjectPlan, String> cell = new TreeTableCell<ProjectPlan, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
System.out.println(getItem());
if (getString().equals("")) {
setGraphic(null);
} else {
ComboBox<String> comboBox = new ComboBox(ADM);
comboBox.setValue(item);
setGraphic(comboBox);
}
}
private String getString() {
return getItem() == null ? "" : getItem();
}
};
return cell;
}
});
It is giving me the proper look as I want but not taking the cell into edit mode. As in the onCommitEdit method is not getting called.