问题
In my application I hava combobox which is holding some values from databse ( some real time updated list ). This ComboBox list is updated every 1 minute. List dosen't have null values. When I'm setting this list to ComboBox..
ComboBox box = new ComboBox(items);
.. there is one extra "empty" row, which is perfectly fine because non value is selected. Right after I'm selecting some value my "empty" value disappears from the list. My main question is How to keep this value on the list?
Why this is a problem..
- Scenerio values is selected in database, first application start
- List is loaded ( wiht selected empty value ).
- Value is selected.
- During first background refresh, empty values disappears, and combobox value is selected to n+1, next value is selected.
- If I want to select empty values I have to all clearSelection from selection model / by some extra control.
回答1:
While it is an old question, I spent quite bit of time trying to solve the same issue in my application and thought i might as well add my solution here.
One possible workaround is to create an extra list that contains null
and the items you wish to be selectable.
ObservableList<String> selectableActivities = FXCollections.observableArrayList("a", "b", "c");
ObservableList<String> selectableActivitiesWithNull = FXCollections.observableArrayList();;
selectableActivitiesWithNull.add(null);
selectableActivitiesWithNull.addAll(selectableActivities);
In order to support updates of the original list you would need a ListChangeListener
that updates the extra list according to changes in the original list.
selectableActivities.addListener((ListChangeListener<String>)(change -> {
while (change.next()) {
if (change.wasPermutated()) {
selectableActivitiesWithNull.sort((a, b) -> {
return Integer.compare(selectableActivities.indexOf(a), selectableActivities.indexOf(b));
});
} else if (change.wasRemoved()) {
selectableActivitiesWithNull.remove(change.getFrom()+1, change.getTo()+2);
} else if (change.wasAdded()) {
selectableActivitiesWithNull.addAll(change.getFrom()+1, selectableActivities.subList(change.getFrom(), change.getTo()));
} else if (change.wasUpdated()) {
for (int i = change.getFrom(); i < change.getTo(); ++i) {
selectableActivitiesWithNull.set(i+1, selectableActivities.get(i));
}
}
}
}));
And finally you use the extra list for the ComboBox items.
ComboBox<String> combobox = new ComboBox<String>();
combobox.setItems(selectableActivitiesWithNull);
Now you can modify the original list as usual and the ComboBox will update accordingly while also having an empty selection as the first item. And most importantly your original list will not be polluted by placeholder objects that could cause issues in other parts of the application.
This will also work with other objects, assuming that you add an apropriate StringConverter
to the ComboBox. Note that the converter must also be able to handle null values if using the above approach.
StringConverter<Object> activityConverter = new StringConverter<Object>() {
@Override
public String toString(Object object) {
return object != null ? object.toString() : "";
}
@Override
public ActivityDataRow fromString(String string) {
return null;
}
};
combobox.setConverter(activityConverter);
While this approach is not exactly what you desired, I believe this is a close you can get without implementing a custom combobox.
回答2:
It's really easy all you have to do is to add a " "
as one of them items, and then select it:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class ComboBoxSample extends Application {
public static void main(String[] args) {
launch(args);
}
@Override public void start(Stage stage) {
stage.setTitle("ComboBoxSample");
Scene scene = new Scene(new Group(), 450, 250);
final ComboBox<String> fooComboBox = new ComboBox<String>();
fooComboBox.getItems().addAll(
" ",
"foo",
"bar",
"FooBar"
);
fooComboBox.setValue(" ");
HBox box = new HBox();
box.getChildren().add(fooComboBox);
Group root = (Group)scene.getRoot();
root.getChildren().add(box);
stage.setScene(scene);
stage.show();
}
}
Example with using the class Person:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class ComboBoxSample extends Application {
ObservableList<Person> people =
FXCollections.observableArrayList(
new Person(null,null),
new Person("Paul", "Mc"),
new Person("Bill", "Apple"),
new Person("Foo", "Bar")
);
@Override
public void start(Stage primaryStage) {
final ComboBox comboBox = new ComboBox(people);
comboBox.getSelectionModel().selectFirst(); //select the first element
comboBox.setCellFactory(new Callback<ListView<Person>,ListCell<Person>>(){
@Override
public ListCell<Person> call(ListView<Person> p) {
final ListCell<Person> cell = new ListCell<Person>(){
@Override
protected void updateItem(Person t, boolean bln) {
super.updateItem(t, bln);
if(t != null){
setText(t.toString());
}else{
setText(null);
}
}
};
return cell;
}
});
final Label label = new Label();
Button btn = new Button();
btn.setText("Read comboBox");
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
if(!(comboBox.getValue().toString()==null))
label.setText("selected: " + comboBox.getValue());
else
label.setText("null");
}
});
VBox vBox = new VBox();
vBox.setPadding(new Insets(5, 5, 5, 5));
vBox.setSpacing(5);
vBox.getChildren().addAll(label, comboBox, btn);
StackPane root = new StackPane();
root.getChildren().add(vBox);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Combo!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Class Person:
public class Person {
private String firstName;
private String lastName;
public Person(String first, String last){
this.lastName = last;
this.firstName = first;
}
public String getName() {
return firstName;
}
public void setName(String name) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setFullName(String first, String last){
this.lastName = last;
this.firstName = first;
}
@Override
public String toString() {
if(firstName != null)
return "" + firstName + " " + lastName;
else
return null;
}
}
来源:https://stackoverflow.com/questions/24217956/how-to-keep-empty-selection-on-my-combobox-list-in-javafx