I want to create a simple ListView with CheckBox for each item. This has been done. Now I am looking for a way to get all selected Items from this ListView.
I have figured out that I can use the method setCellFactory() to add items when they are selected in a separate Collection and remove them when they are unselected. But I think this is an ugly way to do that.
ListView<String> listView = new ListView<>();
String[] toppings = {"Cheese", "Pepperoni", "Black Olives"};
listView.getItems().addAll(toppings);
listView.setCellFactory(CheckBoxListCell.forListView(new Callback<String, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(String item) {
BooleanProperty observable = new SimpleBooleanProperty();
observable.addListener((obs, wasSelected, isNowSelected)
-> System.out.println("Check box for " + item + " changed from " + wasSelected + " to " + isNowSelected)
);
return observable;
}
}));
How can I get the list of selected items from the ListView?
Use this way
public void start(Stage primaryStage) {
try {
ListView<String> listView = new ListView<>();
Button button = new Button("Get");
List<String> list = new ArrayList<>();
String[] toppings = { "Cheese", "Pepperoni", "Black Olives" };
listView.getItems().addAll(toppings);
listView.setCellFactory(CheckBoxListCell.forListView(new Callback<String, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(String item) {
BooleanProperty observable = new SimpleBooleanProperty();
observable.addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
list.add(item);
} else {
list.remove(item);
}
});
return observable;
}
}));
button.setOnAction(e -> {
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
});
VBox root = new VBox();
root.setAlignment(Pos.CENTER);
root.getChildren().addAll(listView, button);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
If you add a boolean property to your model class, e.g.:
public class Topping {
private final String name ;
private final BooleanProperty selected = new SimpleBooleanProperty();
public BooleanProperty selectedProperty() {
return selected ;
}
public final boolean isSelected() {
return selectedProperty().get();
}
public final void setSelected(boolean selected) {
selectedProperty().set(selected);
}
public Topping(String name) {
this.name = name ;
}
public String getName() {
return name ;
}
#Override
public String toString() {
return getName();
}
}
and use it in the check box list cell:
listView.setCellFactory(CheckBoxListCell.forListView(Topping::selectedProperty));
then you can just create a filtered list which contains all the selected toppings:
ListView<Topping> listView = new ListView<>();
listView.getItems().addAll(new Topping("Cheese"), new Topping("Pepperoni"),
new Topping("Black Olives"));
ObservableList<Topping> selectedToppings =
listView.getItems().filtered(Topping::isSelected);
Now selectedToppings will always contain exactly the toppings that have their check boxes checked in the list view.
Unless the selected property is a part of your model you are now blending a user interaction need with your model, which I would try to avoid.
For example, if you would create two lists where you move the items from one or the other, would you still use this property or would be containment in that list be enough for you?
In your (Pizza?) example an order for once confirmed to the user should only contain toppings that are selected, it would be a strange model if your pizza order would contain all possible toppings and the kitchen would have to iterate over the list in order to find the needed toppings. This is a strong indication that the fact that it is selected is not part of the model but a user interaction item.
To avoid mixing user interface and model you could use the CheckListView of the ControlsFX library which has a CheckModel which not only allowes you to query the checked items, but also manipulate it if your logic would require to do so.
Related
I have a cell table with the first column as checkboxes. My checkboxes have to be checked or unchecked when there is any single click event on the entire row. This is the following code for creating a MultiSelectionModel, creating CheckboxCell and creating a column for cell table.
MultiSelectionModel<Object> selectionModel = new MultiSelectionModel<>();
table.setSelectionModel(selectionModel);
CheckboxCell selectedCell = new CheckboxCell();
Column<Object,Boolean> selectedCol = new Column<Object, Boolean>(selectedCell){
#Override
public Boolean getValue(Object object) {
return object.isSelected();
}
};
table.addColumn(selectedCol);
//Single Click Event to Enable/Disable checkbox.
table.addDomHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
Set<Object> selectedItems = selectionModel.getSelectedSet();
for (Object s : selectedItems) {
Window.alert(String.valueOf(s));
selectionModel.setSelected(s, true);
}
}
}, ClickEvent.getType());
I tried to mark a row as checked using "selectionModel.setSelected(s, true)". But it isn’t working, when I clicked on row, the corresponding checkbox is not being checked.
My question is how do I enable/disable checkboxes onclick of an entire row. Is my approach correct. Or Is there any other way to perform this action in GWT.
You are very close to the working solution.
In the selectedCell you should return the value depending on selectionModel:
return selectionModel.isSelected(object);
This way you are using default multi selection model that selects rows by clicking on them. And the checkbox value comes from the selection model. That's it.
See the working example below:
CellTable<String> table = new CellTable<String>();
final MultiSelectionModel<String> selectionModel = new MultiSelectionModel<>();
table.setSelectionModel(selectionModel);
CheckboxCell selectedCell = new CheckboxCell();
Column<String, Boolean> selectedCol = new Column<String, Boolean>(selectedCell) {
#Override
public Boolean getValue(String object) {
// return object.isSelected();
return selectionModel.isSelected(object);
}
};
table.addColumn(selectedCol);
table.addColumn(new TextColumn<String>() {
#Override
public String getValue(String object) {
return object;
}
});
List<String> values = new ArrayList<>();
for(int i = 0; i < 10; i++)
values.add("Line " + (i + 1));
table.setRowData(values);
You can use standard Ctrl and Shift keys to control selection.
I would like to have this functionality in my program:
I will have a user input field. When the user pressed the button, it will be added to the list, and input will be shown to the user.
The problem is, I would like to deselect/remove those input if the user wants. I could not achieve this.
Here is the code I have written so far, I have removed some functionality unnecessary for the question's scope:
public class AddUserInput extends VerticalLayout{
// The user input will be added to the this list
// later, this list will be sent to the server for some verification
private List<String> emails;
private HorizontalLayout content;
private VerticalLayout rows;
// user input field
private TextField emailField = new TextField("Enter email address");
public AddUserInput() {
content = new HorizontalLayout();
rows = new VerticalLayout();
content.setMargin(true);
Button addToListButton= new Button("Add to list");
addToListButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
// When the user clicks add to list button
// The raw input will be added to the emails list
// The UI component is added to 'rows' component
rows.addComponent(addNewRow(emailField.getValue()));
}
});
content.addComponents(emailField, addToListButton, rows);
addComponent(content);
}
public Component addNewRow(String email){
HorizontalLayout newRow = new HorizontalLayout();
Button deleteRowButton = new Button("-");
deleteRowButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
// I can delete from the UI by using the code below
newRow.removeAllComponents();
rows.removeComponent(newRow);
// How to remove from the email list???
}
});
emails.add(emailField.getValue());
Label lastEmail = new Label(emailField.getValue());
emailField.clear();
newRow.addComponents(lastEmail,deleteRowButton);
return newRow;
}
}
Is there any component/library that does this functionality?
I only need a text field, and adding the input to the list, and removing the list item if a user wants to.
The visualization of the code above:
You could use the NativeSelect component for managing the entered Strings.
I modified your AddUserInput-Component to use a NativeSelect and a corresponding DataProvider:
public class AddUserInput extends VerticalLayout {
private HorizontalLayout content = new HorizontalLayout();;
private NativeSelect<String> select = new NativeSelect<>("The List");
private ListDataProvider<String> dataProvider = DataProvider.ofCollection(new ArrayList<>());
private Button addToListButton= new Button("Add to list");
private Button deleteFromListButton = new Button("-");
private TextField emailField = new TextField("Enter email address");
public AddUserInput() {
select.setVisibleItemCount(5);
select.setWidth("100px");
select.setDataProvider(dataProvider);
select.setEmptySelectionAllowed(false);
deleteFromListButton.setEnabled(false);
content.setMargin(true);
addToListButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
addEmailToList(emailField.getValue());
}
});
deleteFromListButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent clickEvent) {
select.getSelectedItem().ifPresent(selectedItem -> removeSelectedEmailFromList());
}
});
select.addValueChangeListener(new HasValue.ValueChangeListener<String>() {
#Override
public void valueChange(HasValue.ValueChangeEvent<String> valueChangeEvent) {
deleteFromListButton.setEnabled(select.getSelectedItem().isPresent());
}
});
content.addComponents(emailField, addToListButton, select, deleteFromListButton);
addComponent(content);
}
private void addEmailToList(String email){
dataProvider.getItems().add(email);
select.getDataProvider().refreshAll();
emailField.clear();
}
private void removeSelectedEmailFromList(){
select.getSelectedItem().ifPresent(selectedItem -> dataProvider.getItems().remove(selectedItem));
select.setSelectedItem(dataProvider.getItems().isEmpty() ? null : dataProvider.getItems().iterator().next());
select.getDataProvider().refreshAll();
}
}
It looks like the following:
Would that be a possible option for you?
I'am working on a java project using javafx multiple input types.but i am having a strangle ComboBox behaviours since i use Labels with images(ImageView) on it.
1- Combobox looks in white! but i need it in black.
2- and every time i choose an item.
3- it disappear!!!
Here is my code:
...
import javafx.scene.control.ComboBox;
import javafx.scene.image.ImageView;
ImageView img_tun = new ImageView("images/icones/flag/Tunisia.png");
Label lbl_tun=new Label("1",img_tun);
ImageView img_fr = new ImageView("images/icones/flag/France.png");
Label lbl_fr=new Label("2",img_fr);
ImageView img_aut = new ImageView("images/icones/flag/World.png");
Label lbl_aut=new Label("3",img_aut);
optionsnat=FXCollections.observableArrayList(lbl_tun,lbl_fr,lbl_aut);
#FXML
ComboBox<Label> cb_nat = new ComboBox<Label>();
private String nat="1";
...
#Override
public void initialize(URL location, ResourceBundle resources) {
...
cb_nat.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observableValue, Number number, Number number2) {
if(cb_nb.getItems().get((Integer) number2)=="1"){setNat("1");}
else if(cb_nb.getItems().get((Integer) number2)=="2"){setNat("2");}
else if(cb_nb.getItems().get((Integer) number2)=="3"){setNat("3");}
else{System.err.println("Erreur lors de changement de nation..");}
}
});
}
...
and code.fxml
<ComboBox fx:id="cb_nat" layoutX="40.0" layoutY="265.0" prefWidth="150.0" />
EDIT:
After reading this Article i know that my approach is tottaly wrong and strongly not recommended.. if anyone have another ideas to put bnation flags in ComboBox please help!!
thanks..(Sorry for my bad english)
What is causing this problem is that when you choose a ListCell, its item (Label in our situation) is being moved by the ComboBox from the ListCell (Items observableList) to the ButtonCell, the ButtonCell is the small box that is empty by default. However, we all know that any Node object cannot be placed twice anywhere inside the same scene, and since there is no clone function for the ListCell class, javafx removes it from its last place to the new place which is the ButtonCell.
The solution is to add strings
items in the list and provide a cell factory to create the label node inside the cell factory. Create a class called "StringImageCell" and do the following:
You need to set the cellFactory property:
cb_nat.setCellFactory(listview -> new StringImageCell());
You need to set the buttonCell property: cb_nat.setButtonCell(new StringImageCell());
Here is an example:
public class ComboBoxCellFactory extends Application {
#Override
public void start(Stage stage) throws Exception {
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().addAll("1", "2", "3");
//Set the cellFactory property
comboBox.setCellFactory(listview -> new StringImageCell());
// Set the buttonCell property
comboBox.setButtonCell(new StringImageCell());
BorderPane root = new BorderPane();
root.setCenter(comboBox);
Scene scene = new Scene(root, 600, 600);
stage.setScene(scene);
stage.show();
}
//A Custom ListCell that displays an image and string
static class StringImageCell extends ListCell<String> {
Label label;
static HashMap<String, Image> pictures = new HashMap<>();
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setItem(null);
setGraphic(null);
} else {
setText(item);
ImageView image = getImageView(item);
label = new Label("",image);
setGraphic(label);
}
}
}
private static ImageView getImageView(String imageName) {
ImageView imageView = null;
switch (imageName) {
case "1":
case "2":
case "3":
if (!pictures.containsKey(imageName)) {
pictures.put(imageName, new Image(imageName + ".png"));
}
imageView = new ImageView(pictures.get(imageName));
break;
default:
imageName = null;
}
return imageView;
}
public static void main(String[] args) {
launch(args);
}
}
The code below runs as its name says on Java 8 update 5 but not on later ones:
public class TableViewShowingOnlyAnAppendContextMenuItemIfRowIsEmptyElseDeleteIsIncluded extends Application {
private final TableView<Name> table = new TableView<>();
private final ObservableList<Name> data = FXCollections.observableArrayList(new Name("Jacob"),
new Name("Isabella"), new Name("Ethan"), new Name("Emma"), new Name("Michael"));
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
TableColumn<Name, String> column = new TableColumn<>("Name");
column.setCellValueFactory(new PropertyValueFactory<>("name"));
table.getColumns().add(column);
table.setItems(data);
ContextMenu contextMenu = new ContextMenu();
contextMenu.getItems().add(new MenuItem("append"));
table.setContextMenu(contextMenu);
table.setRowFactory(tableView -> {
TableRow<Name> row = new TableRow<>();
row.contextMenuProperty().bind(
Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(showOnlyAppendContextMenuItemIfRowIsEmptyElseIncludeDelete())
.otherwise((ContextMenu) null));
return row;
});
Scene scene = new Scene(table);
stage.setScene(scene);
stage.show();
}
private ContextMenu showOnlyAppendContextMenuItemIfRowIsEmptyElseIncludeDelete() {
ContextMenu rowMenu = new ContextMenu();
ContextMenu tableMenu = table.getContextMenu();
if (tableMenu != null)
rowMenu.getItems().addAll(tableMenu.getItems());
rowMenu.getItems().add(new MenuItem("delete"));
return rowMenu;
}
public static class Name {
private final SimpleStringProperty name;
private Name(String name) {
this.name = new SimpleStringProperty(name);
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
} }
Can help me find the error in the code? Or if there is none, is this a regression that should be submitted? As of now, all the PCs in use have 8u5.
Thanks in advance.
This code looks like something I may have posted a while back...
The issue is that using a MenuItem in multiple menus is not really supported. While this isn't explicitly stated in the Javadocs, the fact that MenuItem has a getMenu() method does imply this. The fact that it worked prior to 8u5 is really just luck...
The fix is to create new menu items that are copies of the ones in the table's context menu:
private ContextMenu showOnlyAppendContextMenuItemIfRowIsEmptyElseIncludeDelete() {
ContextMenu rowMenu = new ContextMenu();
ContextMenu tableMenu = table.getContextMenu();
if (tableMenu != null) {
for (MenuItem item : tableMenu.getItems()) {
MenuItem rowItem = new MenuItem(item.getText());
rowItem.setGraphic(item.getGraphic());
rowItem.setOnAction(item.getOnAction());
rowMenu.getItems().add(rowItem);
}
}
rowMenu.getItems().add(new MenuItem("delete"));
return rowMenu;
}
Another approach is to use ControlsFX Actions, so you can maintain a (single) list of actions for the whole table and generate menu items from them for both the table and the rows.
First of all, sorry my english and thanks for reading.. :-)
I have a tableview and it show some information about a class named Produto.
The table has a column where is displayed a image with the name of produto, but I need to show this image only for some type of Produto.
The Produto class:
public class Produto {
private Integer id;
private String nome;
private Tipo type;
//get set..
}
The column of table:
TableColumn<Produto,String> tbcNomeProduto = new TableColumn<Produto,String>();
tbcNomeProduto.setCellValueFactory(new PropertyValueFactory<Produto, String>("nome"));
tbcNomeProduto.setCellFactory(new Callback<TableColumn<Produto,String>,TableCell<Produto,String>>(){
#Override
public TableCell<Produto, String> call(TableColumn<Produto, String> param) {
TableCell<Produto, String> cell = new TableCell<Produto, String>(){
#Override
public void updateItem(String item, boolean empty) {
if(item != null){
HBox box= new HBox();
box.setSpacing(10);
VBox vbox = new VBox();
vbox.getChildren().add(new Label(item));
ImageView imageview = new ImageView();
imageview.setImage(new Image(getClass().getResourceAsStream("16x16.png")));
box.getChildren().addAll(imageview,vbox);
setGraphic(box);
}
}
};
return cell;
}
});
How could I access the current Produto object inside the updateItem, to get the type of Produto and choose if display or not the image on the table?
The way I usually approach this is to make the type of the table column the same as the type of the table:
TableColumn<Produto, Produto> tbcNomeProduto = new TableColumn<>();
The reason for this is that the value displayed in the cells in this column depends on two things: Produto.nome and Produto.type; in other words the cells in this column are views of both Produto.nome and Produto.type. Thus the smallest entity containing all the data needed to display the cell (i.e. the smallest entity of which the cell is a view) is the Produto instance itself.
So now I would do
tbcNomeProduto.setCellValueFactory(new Callback<CellDataFeatures<Produto, Produto>, ObservableValue<Produto>>() {
#Override
public ObservableValue<Produto> call(CellDataFeatures<Produto, Produto> data) {
return new ReadOnlyObjectWrapper<>(data.getValue());
}
});
tbcNomeProduto.setCellFactory(new Callback<TableColumn<Produto, Produto>, TableCell<Produto, Produto>>() {
#Override
public TableCell<Produto, Produto> call(TableColumn<Produto, Produto> col) {
return new TableCell<Produto, Produto>() {
private HBox hbox = new HBox();
private ImageView imageView = new ImageView(new Image(getClass().getResourceAsStream("16x16.png")));
private Label label = new Label();
// anonymous constructor:
{
setGraphic(hbox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
public void updateItem(Produto item, boolean empty) {
super.updateItem(item, empty);
hbox.getChildren().clear();
if (item != null) {
Tipo type = item.getType();
String nome = item.getNome();
if (/* should show image...*/) {
hbox.getChildren().add(imageView);
}
hbox.getChildren().add(label);
}
}
};
}
});
In your example, your Produto class is a POJO, following the standard JavaBean conventions and not using observable JavaFX Properties. Thus if the nome field were to change for a displayed Produto instance, there would be no opportunity for the table to update anyway, as the nome field is not observable.
If you were to have observable fields in the Produto class, and those values could change while the object was displayed, then you would need to do a bit more work in the table cell. This is because the nome field could change without the Produto instance changing; the latter means that the updateItem(...) method would not be invoked. I want to include the code to manage this for other readers who may come across this question, even though it's not relevant for the question at hand. Using anonymous inner classes for this gets very ugly in a hurry, so I'm going to revert to Java 8 code and use lambda expressions for this section.
So suppose the Produto class looks like
public class Produto {
private final StringProperty nome = new SimpleStringProperty();
public StringProperty nomeProperty() {
return nome ;
}
public final String getNome() {
return nomeProperty().get();
}
public final void setNome(String nome) {
nomeProperty().set(nome);
}
private final ObjectProperty<Tipo> type = new SimpleObjectProperty<>() ;
// get/set and property accessor methods as above....
private final IntegerProperty id = new SimpleIntegerProperty();
// get/set and property accessor methods...
}
As above you have
TableColumn<Produto, Produto> tbcNomeProduto = new TableColumn<>();
tbcNomeProduto.setCellValueFactory(cellData -> new ReadOnlyPropertyWrapper<>(cellData.getValue()));
For the cell factory, you need to create listeners for the individual properties. Register and deregister those properties when the Produto that is displayed changes:
tbc.setCellFactory(col -> {
Label label = new Label();
ImageView imageView = new ImageView(new Image(getClass().getResourceAsStream("16x16.png")));
HBox hbox = new HBox();
TableCell<Produto, Produto> cell = new TableCell<>();
// listener for the nome property changing:
ChangeListener<String> nomeListener = (obs, oldNome, newNome) -> label.setText(newNome);
// listener for type property changing:
ChangeListener<Tipo> typeListener = (obs, oldType, newType) -> {
if ( /* should show image */) {
hbox.getChildren().setAll(imageView, label);
} else {
hbox.getChildren().setAll(label);
}
}
cell.itemProperty().addListener((obs, oldProduto, newProduto) -> {
if (oldProduto != null) {
oldProduto.nomeProperty().removeListener(nomeListener);
oldProduto.typeProperty().removeListener(typeListener);
}
if (newProduto == null) {
cell.setGraphic(null);
} else {
label.setText(newProduto.getNome());
Tipo type = newProduto.getType();
if (/* should show graphic */) {
hbox.getChildren().setAll(imageView, label);
} else {
hbox.getChildren().setAll(label);
}
cell.setGraphic(hbox);
newProduto.nomeProperty().addListener(nomeListener);
newProduto.typeProperty().addListener(typeListener);
}
});
cell.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
return cell ;
});
This generalizes fairly easily to display different images under different conditions, or even to manipulate css styles for the cell, etc.