JavaFX可搜索的组合框(如js select2)

时间:2016-06-13 21:21:58

标签: javafx

我正在为javafx寻找像select2这样的组件。

一个组合框,当弹出窗口出现时会有一个可搜索的文本字段,并在下面过滤了listview。

已经完成的任何想法或事情?

1 个答案:

答案 0 :(得分:0)

我几个月前自己实现了这个。这个想法基本上就是将ComboBox下拉列表包装在FilteredList中,然后向ComboBox#textProperty()添加一个更改FilteredList谓词的侦听器。

我的类包含许多额外的功能,例如将小写输入转换为大写并限制输入长度的选项。如果您不需要,可以删除这些部分。

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class MCVE extends Application {

    @Override
    public void start(Stage stage) {
        ComboBox<String> box = new ComboBox<String>();
        box.setEditable(true);

        // For the combo box filter to work properly we need to create the item
        // list and wrap it in a FilteredList.
        ObservableList<String> items = FXCollections.observableArrayList("One", "Two", "Three", "OneTwo", "ThreeTwo",
                "OneTwoThree");
        FilteredList<String> filteredItems = new FilteredList<String>(items);

        // Then you need to provide the InputFilter with the FilteredList in the
        // constructor call.
        box.getEditor().textProperty().addListener(new InputFilter(box, filteredItems, false));

        box.setItems(filteredItems);

        BorderPane view = new BorderPane();
        view.setCenter(box);

        stage.setScene(new Scene(view));
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }

    /**
     * 
     * @author Jonatan Stenbacka
     *
     */
    class InputFilter implements ChangeListener<String> {

        private ComboBox<String> box;
        private FilteredList<String> items;
        private boolean upperCase;
        private int maxLength;
        private String restriction;

        /**
         * @param box
         *            The combo box to whose textProperty this listener is
         *            added.
         * @param items
         *            The {@link FilteredList} containing the items in the list.
         */
        public InputFilter(ComboBox<String> box, FilteredList<String> items, boolean upperCase, int maxLength,
                String restriction) {
            this.box = box;
            this.items = items;
            this.upperCase = upperCase;
            this.maxLength = maxLength;
            this.restriction = restriction;
        }

        public InputFilter(ComboBox<String> box, FilteredList<String> items, boolean upperCase, int maxLength) {
            this(box, items, upperCase, maxLength, null);
        }

        public InputFilter(ComboBox<String> box, FilteredList<String> items, boolean upperCase) {
            this(box, items, upperCase, -1, null);
        }

        public InputFilter(ComboBox<String> box, FilteredList<String> items) {
            this(box, items, false);
        }

        @Override
        public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
            StringProperty value = new SimpleStringProperty(newValue);

            // If any item is selected we save that reference.
            String selected = box.getSelectionModel().getSelectedItem() != null
                    ? box.getSelectionModel().getSelectedItem() : null;

            String selectedString = null;
            // We save the String of the selected item.
            if (selected != null) {
                selectedString = (String) selected;
            }

            if (upperCase) {
                value.set(value.get().toUpperCase());
            }

            if (maxLength >= 0 && value.get().length() > maxLength) {
                value.set(oldValue);
            }

            if (restriction != null) {
                if (!value.get().matches(restriction + "*")) {
                    value.set(oldValue);
                }
            }

            // If an item is selected and the value in the editor is the same
            // as the selected item we don't filter the list.
            if (selected != null && value.get().equals(selectedString)) {
                // This will place the caret at the end of the string when
                // something is selected.
                Platform.runLater(() -> box.getEditor().end());
            } else {
                items.setPredicate(item -> {
                    String itemString = item;
                    if (itemString.toUpperCase().contains(value.get().toUpperCase())) {
                        return true;
                    } else {
                        return false;
                    }
                });
            }

            // If the popup isn't already showing we show it.
            if (!box.isShowing()) {
                // If the new value is empty we don't want to show the popup,
                // since
                // this will happen when the combo box gets manually reset.
                if (!newValue.isEmpty() && box.isFocused()) {
                    box.show();
                }
            }
            // If it is showing and there's only one item in the popup, which is
            // an
            // exact match to the text, we hide the dropdown.
            else {
                if (items.size() == 1) {
                    // We need to get the String differently depending on the
                    // nature
                    // of the object.
                    String item = items.get(0);

                    // To get the value we want to compare with the written
                    // value, we need to crop the value according to the current
                    // selectionCrop.
                    String comparableItem = item;

                    if (value.get().equals(comparableItem)) {
                        Platform.runLater(() -> box.hide());
                    }
                }
            }

            box.getEditor().setText(value.get());
        }
    }
}