How can I make adding a value to items in a combo box possible so the user can either select from the existing items or clique "Add element" item to add a new item?
private ComboBox<String> comboStructDonnees;
Followed by:
comboData.getItems().addAll("TVW", "VWT", "TTVW", "VWXT", "Add item");
I don't know which event should I create next, I want to the text to be entered on the added element if possible.
Any help would be appreciated.
You can add an item with a "special value" (e.g. an empty string) to the end of the list of items for the combo box.
Use a cell factory to create a cell that displays a user-friendly message ("Add item..", for example) to the user when that value is displayed. Add an event filter to the cell that displays a dialog for inputting a new value if the cell is displaying the special value.
Here's a quick SSCCE:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.TextInputDialog;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class AddItemToComboBox extends Application {
#Override
public void start(Stage primaryStage) {
ComboBox<String> combo = new ComboBox<>();
combo.getItems().addAll("One", "Two", "Three", "");
combo.setCellFactory(lv -> {
ListCell<String> cell = new ListCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
if (item.isEmpty()) {
setText("Add item...");
} else {
setText(item);
}
}
}
};
cell.addEventFilter(MouseEvent.MOUSE_PRESSED, evt -> {
if (cell.getItem().isEmpty() && ! cell.isEmpty()) {
TextInputDialog dialog = new TextInputDialog();
dialog.setContentText("Enter item");
dialog.showAndWait().ifPresent(text -> {
int index = combo.getItems().size()-1;
combo.getItems().add(index, text);
combo.getSelectionModel().select(index);
});
evt.consume();
}
});
return cell ;
});
BorderPane root = new BorderPane();
root.setTop(combo);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Related
For my project in Java FX, I have a list of strings and I need to add them to a combo box with the requirement that only one of them (the first) be coloured in red.
I thought about encapsulating the strings in a Text and adding them to the combo box with appropriate setStyle("fx-text-fill: Color.xxx"). This calls for a setCellFactory() method and I don't know how to set up it correctly.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.Pane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Callback;
public class Mainu extends Application
{
final ObservableList<Text> SAMPUNITFRONT = FXCollections.observableArrayList(
new Text("complete"), new Text("seconds"), new Text("minutes"), new Text("hours"), new Text("days"));
#Override
public void start(Stage stage) throws Exception
{
ComboBox<Text> cb = new ComboBox<Text>();
for(int j = 0; j < SAMPUNITFRONT.size(); j++) // cycle over the list and generate a line with dashing defined by list
{
Text text = SAMPUNITFRONT.get(j);
if(text.getText().equals("complete"))
text.setStyle("-fx-text-fill: RED");
else
text.setStyle("-fx-text-fill: BLACK");
cb.getItems().add(text);
}
cb.setCellFactory(new Callback<ListView<Text>, ListCell<Text>>()
{
#Override public ListCell<Text> call(ListView<Text> p)
{
return new ListCell<Text>()
{
private final Text text;
{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
text = new Text();
} // end Text
#Override protected void updateItem(Text item, boolean empty)
{
super.updateItem(item, empty);
if (item == null || empty)
{
setGraphic(null);
}
else
{
text.setStyle(item.getStyle());
setGraphic(text);
setItem(text);
}
} // end updateItem()
}; // end ListCell return
}
});
cb.getSelectionModel().selectFirst();
Pane root = new Pane();
root.getChildren().add(cb);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {launch(args);}
}
At the moment, the drop down list of the combo is empty.
Several recommendations to make here:
Do not use a type of Node as item type. Instead store the data needed to determine the look of the ListCell in the items and let the ListCell implementations deal with the rest. You could simply use String as item type, if you the coloration should be done based on the text or index only or you could create a class containing 2 properties.
Do not call setItem yourself. Let the ComboBox/ListView deal with this.
For Text you need to use the -fx-fill css property instead of -fx-text-fill. The latter one would work, if you use the text property of the ListCell itself.
If you do not use the functionality of ObservableList, it's pointless to create one. You could simply have used List and Arrays.asList instead of ObservableList and FXCollections.observableArrayList for SAMPUNITFRONT
final ObservableList<String> SAMPUNITFRONT = FXCollections.observableArrayList("complete",
"seconds", "minutes", "hours", "days");
#Override
public void start(Stage stage) throws Exception {
ComboBox<String> cb = new ComboBox<String>(SAMPUNITFRONT);
cb.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> p) {
return new ListCell<String>() {
private final Text text;
{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
text = new Text();
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
} else {
text.setStyle(item.equals("complete") ? "-fx-fill: red" : "-fx-fill: black");
text.setText(item);
setGraphic(text);
}
}
};
}
});
cb.getSelectionModel().selectFirst();
Pane root = new Pane();
root.getChildren().add(cb);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Alternatively without using a Text as graphic:
return new ListCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(item);
if (item != null) {
setStyle(item.equals("complete") ? "-fx-text-fill: red" : "-fx-text-fill: black");
}
}
};
The problem is that you set style to Text objects. Try set the same style to cells. And you don't need encapsulating the strings in a Text.
Here is your code with fixes:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Main extends Application
{
final ObservableList<String> SAMPUNITFRONT = FXCollections.observableArrayList("complete", "seconds", "minutes", "hours", "days");
#Override
public void start(Stage stage)
{
ComboBox<String> cb = new ComboBox<>();
cb.setCellFactory(cell -> new ListCell<String>()
{
#Override
protected void updateItem(String item, boolean empty)
{
super.updateItem(item, empty);
if (item == null || empty)
{
setGraphic(null);
setText("");
}
else
{
if (item.equals("complete"))
{
setStyle("-fx-text-fill: RED");
}
setText(item);
}
}
});
cb.getItems().addAll(SAMPUNITFRONT);
cb.getSelectionModel().selectFirst();
Pane root = new Pane();
root.getChildren().add(cb);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
And result:
I hope I understood you ritghtly and could help.
My question is how to generate buttons, set with car pictures, based on the checkboxes and/or radio buttons selected by a user in javafx?
I'm simulating a car dealership website with car pictures. The user should be able to filter the pictures displayed by clicking checkboxes and/or radio buttons selection.
I'm first creating all the picture buttons with a for each loop. I could use if and if/else statements to filter through the pictures but there would be duplicates. I've heard of observablelist but I haven't learned those yet.
Can someone help me out with this one please? Thank you!
ArrayList<Car> cars;
for (Car r : cars)
{
for (int i = 0; i < SIZE; i++)
{
// create buttons and set car pictures
carButton[i] = new Button();
carButton[i].setId(String.format("%d", i));
carButton[i].setGraphic(cars.get(i).getCarPicture());
}
}
Instead of using an ArrayList for your cars, I recommend using an ObservableList:
ObservableList<Car> carsList = FXCollections.observableArrayList<>();
An ObservableList allows you to listen for changes and respond accordingly. For example, when a new Car is added to the list, you could trigger an event that automatically adds a new Button to your scene.
Here is a short demo application that shows how this would work. I did comment the code below as well and many of the concepts being used may be beyond your level, but it's one method, at least.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.ImageView;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// Create an ObservableList to hold our Cars
ObservableList<Car> carsList = FXCollections.observableArrayList();
// For our sample, let's use a FlowPane to display all of our buttons. We will add new buttons to this FlowPane
// automatically as new Cars are added to carsList
FlowPane flowPane = new FlowPane();
flowPane.setHgap(10);
flowPane.setVgap(5);
flowPane.setAlignment(Pos.TOP_CENTER);
// Create a ListChangeListener for our carsList. This allows us to perform some actions whenever an item is added
// to or removed from the list. For our example, we will only do something when a new Car is added.
carsList.addListener(new ListChangeListener<Car>() {
#Override
public void onChanged(Change<? extends Car> c) {
System.out.println(carsList.size());
// Get the first change
c.next();
// If an item was added to the list...
if (c.wasAdded()) {
// Create a new button and add it to the FlowPane
// The Change (c) provides access to a List of items that were added during this change. Since we
// are only going to add one Car at a time, we only need to get the first item from the AddedSubList
Button button = new Button(c.getAddedSubList().get(0).getName());
button.setGraphic(c.getAddedSubList().get(0).getIcon());
button.setOnAction(event -> {
// The code to be executed when this button is clicked goes here
});
// Add the button to our FlowPane
flowPane.getChildren().add(button);
}
}
});
// Now we need a Button that will add a new car to the List
Button button = new Button("Add Car");
button.setOnAction(event -> {
// We'll just add a random car to the carsList
carsList.add(new Car("Car #" + (carsList.size() + 1), new ImageView("icon.png")));
});
// Add our FlowPane and Button to the root layout
root.getChildren().addAll(button, flowPane);
primaryStage.setScene(new Scene(root, 550, 250));
primaryStage.show();
}
}
class Car {
private final String name;
private final ImageView icon;
public Car(String name, ImageView icon) {
this.name = name;
this.icon = icon;
}
public String getName() {
return name;
}
public ImageView getIcon() {
return icon;
}
}
The Results: (after clicking the "Add Car" button a few times)
This is a terrible implementation but It will give you some ideas on how to do things. You need to research FilteredList, ListView, and Predicate. This implementation does not handle more than one CheckBox at a time. It will only display the last CheckBox action.
CarList
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
*
* #author Sedrick
*/
public class CarList extends Application {
#Override
public void start(Stage primaryStage) {
List<Car> cars = new ArrayList();
cars.add(new Car("Honda", "2004"));
cars.add(new Car("Ford", "2005"));
cars.add(new Car("Ford", "2004"));
cars.add(new Car("Honda", "2005"));
cars.add(new Car("Toyota", "2004"));
cars.add(new Car("Cadillac", "2005"));
ListView<Car> view = new ListView();
view.setCellFactory((ListView<Car> param) -> {
ListCell<Car> cell = new ListCell<Car>() {
CarView carView = new CarView();
#Override
protected void updateItem(Car item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText("");
carView.setMake(item.getMake());
carView.setModel(item.getModel());
carView.setImageView(item.getUrl());
setGraphic(carView);
} else {
setText("");
setGraphic(null);
}
}
};
return cell;
});
ObservableList<Car> data = FXCollections.observableArrayList(cars);
FilteredList<Car> filteredList = new FilteredList(data);
view.setItems(filteredList);
HBox.setHgrow(view, Priority.ALWAYS);
CheckBox checkBox = new CheckBox("Honda");
checkBox.selectedProperty().addListener((observable, oldValue, newValue) -> {
if(newValue)
{
filteredList.setPredicate((item) -> {
return item.getMake().equals("Honda");
});
}
else{
filteredList.setPredicate((item) -> {
return true;
});
}
});
CheckBox checkBox2 = new CheckBox("Ford");
checkBox2.selectedProperty().addListener((observable, oldValue, newValue) -> {
if(newValue)
{
filteredList.setPredicate((item) -> {
return item.getMake().equals("Ford");
});
}
else{
filteredList.setPredicate((item) -> {
return true;
});
}
});
CheckBox checkBox3 = new CheckBox("2004");
checkBox3.selectedProperty().addListener((observable, oldValue, newValue) -> {
if(newValue)
{
filteredList.setPredicate((item) -> {
return item.getModel().equals("2004");
});
}
else{
filteredList.setPredicate((item) -> {
return true;
});
}
});
CheckBox checkBox4 = new CheckBox("2005");
checkBox4.selectedProperty().addListener((observable, oldValue, newValue) -> {
if(newValue)
{
filteredList.setPredicate((item) -> {
return item.getModel().equals("2005");
});
}
else{
filteredList.setPredicate((item) -> {
return true;
});
}
});
VBox leftPanel = new VBox(checkBox, checkBox2, checkBox3, checkBox4);
HBox root = new HBox(leftPanel, view);
Scene scene = new Scene(root, 625, 500);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
CarView
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
/**
*
* #author Sedrick
*/
final public class CarView extends HBox{
Label make = new Label();
Label model = new Label();
ImageView imageView = new ImageView();
public CarView(String make, String model, String url) {
this.make.setText(make);
this.model.setText(model);
HBox row1 = new HBox(new Label("Make: "), this.make);
HBox row2 = new HBox(new Label("Model: "), this.model);
VBox vbox = new VBox(row1, row2);
vbox.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
StackPane stackPane1 = new StackPane(vbox);
HBox.setHgrow(stackPane1, Priority.ALWAYS);
Image image = new Image(url);
this.imageView.setImage(image);
this.imageView.setFitHeight(100);
this.imageView.setFitWidth(200);
StackPane stackPane2 = new StackPane(this.imageView);
stackPane2.setStyle("-fx-background-color: yellow");
getChildren().addAll(stackPane1, stackPane2);
setPrefSize(500, 125);
}
public CarView()
{
HBox row1 = new HBox(new Label("Make: "), this.make);
HBox row2 = new HBox(new Label("Model: "), this.model);
VBox vbox = new VBox(row1, row2);
vbox.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
StackPane stackPane1 = new StackPane(vbox);
HBox.setHgrow(stackPane1, Priority.ALWAYS);
this.imageView.setFitHeight(100);
this.imageView.setFitWidth(200);
StackPane stackPane2 = new StackPane(this.imageView);
stackPane2.setStyle("-fx-background-color: yellow");
getChildren().addAll(stackPane1, stackPane2);
setPrefSize(500, 125);
}
public void setImageView(String url) {
Image image = new Image(url);
this.imageView.setImage(image);
}
public void setMake(String make) {
this.make.setText(make);
}
public void setModel(String model)
{
this.model.setText(model);
}
}
Car
/**
*
* #author Sedrick
*/
public class Car {
private String make;
private String model;
private String url = "https://cdn.pixabay.com/photo/2012/05/29/00/43/car-49278_960_720.jpg";
public Car(String make, String model) {
this.make = make;
this.model = model;
}
public String getMake() {
return make;
}
public String getModel() {
return model;
}
public String getUrl()
{
return url;
}
public void setMake(String make) {
this.make = make;
}
public void setModel(String model) {
this.model = model;
}
}
I would like to know if there is any way to style a JavaFX ComboBox programmatically. I have tried to use the method setStyle(String); and styled the button, but it doesn't affect the list
Is there any way to do that?
you can change (for example) the text fill color of the cells of the list in the ComboBox like this:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Callback;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group(), 200, 200);
ComboBox<String> myComboBox = new ComboBox<String>();
myComboBox.getItems().addAll("A", "B", "C", "D", "E");
myComboBox
.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> param) {
final ListCell<String> cell = new ListCell<String>() {
{
super.setPrefWidth(100);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item);
if (item.contains("A")) {
setTextFill(Color.RED);
} else if (item.contains("B")) {
setTextFill(Color.GREEN);
} else {
setTextFill(Color.BLACK);
}
} else {
setText(null);
}
}
};
return cell;
}
});
Group root = (Group) scene.getRoot();
root.getChildren().add(myComboBox);
stage.setScene(scene);
stage.show();
}
}
I think better way how to do it is set CSS ID or CSS class in code. For example your comboBox.
yourComboBox.setId("fancybox");
or set class:
yourComboBox.getStyleClass().clear();
yourComboBox.getStyleClass().add("fancyboxes");
and then style them in CSS.
then you can style almost everything on comboBox.
Example:
#fancyBox .cell {
-fx-text-fill: #4059a9;
}
there are many different "extensions" what you can add after #fancyBox and then style it. (Extension I mean that ".cell" after #fancyBox)
this can help you. Just keep searching.
Javafx combobox styling
I would like to create a ComboBox with the remove button like the picture below:
The picture uses Java Swing, and I don't know how to do this with JavaFX. I would like to create two ComboBoxes (a,b). When I click the "cross" in ComboBox a, I would like to remove a's item and add this item to ComboBox b, and ComboBox b so on.
ComboBox a:
(1)click item then remove it from a and add on b
ComboBox b:
(1)click item then do something(ex:print item)
(2)click cross then remove it from b and add on a
package UnitTest;
import Peer.Peer_Manager;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.chart.XYChart;
import javafx.geometry.Insets;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
public class temp extends Application {
final int height = 200;
final int weight = 300;
final int offset = 5;
Peer_Manager p_management;
XYChart.Series series_hop;
XYChart.Series series_gd;
#Override
public void start(Stage primaryStage) {
VBox vbox = new VBox();
vbox.setPadding(new Insets(5, 5, 5, 5));
vbox.setStyle("-fx-background-color: CORNSILK;");
Scene scene = new Scene(vbox, weight, height);
primaryStage.setScene(scene);
HBox hbBtn = new HBox();
Text t1=new Text(" A:");
Text t2=new Text(" B:");
String[] filename = {"A","B","C"};//conf.load_all();
ComboBox<String> cb = new ComboBox<String>();
cb.setItems(FXCollections.observableArrayList(filename));
cb.setVisibleRowCount(10);
ComboBox<String> cb2 = new ComboBox<String>();
cb.setVisibleRowCount(10);
vbox.getChildren().add(hbBtn);
hbBtn.getChildren().add(t1);
hbBtn.getChildren().add(cb);
hbBtn.getChildren().add(t2);
hbBtn.getChildren().add(cb2);
cb.setOnAction(e -> {
try {
Object object = cb.getValue();
if (object != null) {
cb2.getItems().add(object);
cb.getSelectionModel().clearSelection();
cb.getItems().remove(object);
}
} catch (Exception e1) {
e1.printStackTrace();
}
});
//would like to do something(ex:print item),but don't remove
//add the "cross" beside items,click "cross" to remove item and add on cb
cb2.setOnAction(e -> {
try {
Object object = cb2.getValue();
System.out.println(object);
if (object != null) {
cb1.getItems().add(object);
cb2.getSelectionModel().clearSelection();
cb2.getItems().remove(object);
}
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
});
primaryStage.setTitle("SimulatorFX");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The correct way is to use a CellFactory and create graphic nodes that contain the elements you wish to have. Here is an example:
public void start(Stage primaryStage) throws Exception {
ComboBox<String> cba = new ComboBox<>();
ComboBox<String> cbb = new ComboBox<>();
cba.getItems().addAll("A", "B", "C");
cbb.getItems().addAll("123", "456", "789");
// Set a cell factory for ComboBox A. A similar thing should be done for B.
cba.setCellFactory(lv ->
new ListCell<String>() {
// This is the node that will display the text and the cross.
// I chose a hyperlink, but you can change to button, image, etc.
private HBox graphic;
// this is the constructor for the anonymous class.
{
Label label = new Label();
// Bind the label text to the item property. If your ComboBox items are not Strings you should use a converter.
label.textProperty().bind(itemProperty());
// Set max width to infinity so the cross is all the way to the right.
label.setMaxWidth(Double.POSITIVE_INFINITY);
// We have to modify the hiding behavior of the ComboBox to allow clicking on the hyperlink,
// so we need to hide the ComboBox when the label is clicked (item selected).
label.setOnMouseClicked(event -> cba.hide());
Hyperlink cross = new Hyperlink("X");
cross.setVisited(true); // So it is black, and not blue.
cross.setOnAction(event ->
{
// Since the ListView reuses cells, we need to get the item first, before making changes.
String item = getItem();
System.out.println("Clicked cross on " + item);
if (isSelected()) {
// Not entirely sure if this is needed.
cba.getSelectionModel().select(null);
}
// Remove the item from A and add to B. You can add any additional logic in here.
cba.getItems().remove(item);
cbb.getItems().add(item);
}
);
// Arrange controls in a HBox, and set display to graphic only (the text is included in the graphic in this implementation).
graphic = new HBox(label, cross);
graphic.setHgrow(label, Priority.ALWAYS);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(graphic);
}
}
});
// We have to set a custom skin, otherwise the ComboBox disappears before the click on the Hyperlink is registered.
cba.setSkin(new ComboBoxListViewSkin<String>(cba) {
#Override
protected boolean isHideOnClickEnabled() {
return false;
}
});
VBox vb = new VBox(cba, cbb);
primaryStage.setScene(new Scene(vb));
primaryStage.show();
}
I have created ListView of Labels using :
ListView<Label> list = new ListView<Label>();
Image folder = new Image(getClass().getResourceAsStream("folder.png"));
ObservableList<Label> data = FXCollections.observableArrayList();
for (int i = 0; i < 6; i++) {
Label lbl = new Label();
lbl.setText("label" + i);
lbl.setGraphic(new ImageView(folder));
lbl.setContentDisplay(ContentDisplay.LEFT);
lbl.setGraphicTextGap(10.2);
data.add(lbl);
}
list.setItems(data);
I want the user to be able to double click on any of the Labels within the ListView, the selected Label should be replaced with a TextField so that the user can enter a new label name dynamically.
After the user presses Enter the TextField should turn back into a Label.
Don't use Label as the type of data for the ListView. Use String. Then you can just use the standard TextFieldListCell which has exactly the functionality you describe. Since you want a graphic in the standard cell display, just subclass TextFieldListCell and override the appropriate methods to include the graphic when the text field is not displayed:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import javafx.util.converter.DefaultStringConverter;
public class EditableListViewTest extends Application {
#Override
public void start(Stage primaryStage) {
ListView<String> list = new ListView<>();
Image testImg = new Rectangle(12, 12, Color.CORNFLOWERBLUE).snapshot(null, null);
for (int i = 0; i < 6; i++) {
list.getItems().add("label "+i);
}
StringConverter<String> identityStringConverter = new DefaultStringConverter();
list.setCellFactory(lv -> new TextFieldListCell<String>(identityStringConverter) {
private ImageView imageView = new ImageView(testImg);
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (! empty && ! isEditing()) {
setStaticGraphic();
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
setStaticGraphic();
}
#Override
public void commitEdit(String newValue) {
super.commitEdit(newValue);
setStaticGraphic();
}
private void setStaticGraphic() {
setGraphic(imageView);
setContentDisplay(ContentDisplay.LEFT);
setGraphicTextGap(10.2);
}
});
list.setEditable(true);
primaryStage.setScene(new Scene(new BorderPane(list), 250, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}