How to keep “empty” selection on my combobox list in JavaFx?

感情迁移 提交于 2020-03-21 05:49:23

问题


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..

  1. Scenerio values is selected in database, first application start
    1. List is loaded ( wiht selected empty value ).
    2. Value is selected.
    3. During first background refresh, empty values disappears, and combobox value is selected to n+1, next value is selected.
  2. 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!