java - JavaFX 中的自动完成组合框

标签 java combobox autocomplete javafx-2 javafx-8

我正在寻找一种将自动完成添加到 JavaFX ComboBox 的方法。

这个 AutoFillBox是已知的,但不是我正在搜索的。 我想要的是一个可编辑的组合框,并且在键入列表时应该过滤。但我也想在不输入和查看整个项目的情况下打开列表。

有什么想法吗?

最佳答案

首先,您必须在项目中创建此类:

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.control.ComboBox;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

public class FxUtilTest {

    public interface AutoCompleteComparator<T> {
        boolean matches(String typedText, T objectToCompare);
    }

    public static<T> void autoCompleteComboBoxPlus(ComboBox<T> comboBox, AutoCompleteComparator<T> comparatorMethod) {
        ObservableList<T> data = comboBox.getItems();

        comboBox.setEditable(true);
        comboBox.getEditor().focusedProperty().addListener(observable -> {
            if (comboBox.getSelectionModel().getSelectedIndex() < 0) {
                comboBox.getEditor().setText(null);
            }
        });
        comboBox.addEventHandler(KeyEvent.KEY_PRESSED, t -> comboBox.hide());
        comboBox.addEventHandler(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() {

            private boolean moveCaretToPos = false;
            private int caretPos;

            @Override
            public void handle(KeyEvent event) {
                if (event.getCode() == KeyCode.UP) {
                    caretPos = -1;
                    if (comboBox.getEditor().getText() != null) {
                        moveCaret(comboBox.getEditor().getText().length());
                    }
                    return;
                } else if (event.getCode() == KeyCode.DOWN) {
                    if (!comboBox.isShowing()) {
                        comboBox.show();
                    }
                    caretPos = -1;
                    if (comboBox.getEditor().getText() != null) {
                        moveCaret(comboBox.getEditor().getText().length());
                    }
                    return;
                } else if (event.getCode() == KeyCode.BACK_SPACE) {
                    if (comboBox.getEditor().getText() != null) {
                        moveCaretToPos = true;
                        caretPos = comboBox.getEditor().getCaretPosition();
                    }
                } else if (event.getCode() == KeyCode.DELETE) {
                    if (comboBox.getEditor().getText() != null) {
                        moveCaretToPos = true;
                        caretPos = comboBox.getEditor().getCaretPosition();
                    }
                } else if (event.getCode() == KeyCode.ENTER) {
                    return;
                }

                if (event.getCode() == KeyCode.RIGHT || event.getCode() == KeyCode.LEFT || event.getCode().equals(KeyCode.SHIFT) || event.getCode().equals(KeyCode.CONTROL)
                        || event.isControlDown() || event.getCode() == KeyCode.HOME
                        || event.getCode() == KeyCode.END || event.getCode() == KeyCode.TAB) {
                    return;
                }

                ObservableList<T> list = FXCollections.observableArrayList();
                for (T aData : data) {
                    if (aData != null && comboBox.getEditor().getText() != null && comparatorMethod.matches(comboBox.getEditor().getText(), aData)) {
                        list.add(aData);
                    }
                }
                String t = "";
                if (comboBox.getEditor().getText() != null) {
                    t = comboBox.getEditor().getText();
                }

                comboBox.setItems(list);
                comboBox.getEditor().setText(t);
                if (!moveCaretToPos) {
                    caretPos = -1;
                }
                moveCaret(t.length());
                if (!list.isEmpty()) {
                    comboBox.show();
                }
            }

            private void moveCaret(int textLength) {
                if (caretPos == -1) {
                    comboBox.getEditor().positionCaret(textLength);
                } else {
                    comboBox.getEditor().positionCaret(caretPos);
                }
                moveCaretToPos = false;
            }
        });
    }

    public static<T> T getComboBoxValue(ComboBox<T> comboBox){
        if (comboBox.getSelectionModel().getSelectedIndex() < 0) {
            return null;
        } else {
            return comboBox.getItems().get(comboBox.getSelectionModel().getSelectedIndex());
        }
    }

}

要使您的 ComboBox 自动完成,请像这样使用它:

FxUtilTest.autoCompleteComboBoxPlus(myComboBox, (typedText, itemToCompare) -> itemToCompare.getName().toLowerCase().contains(typedText.toLowerCase()) || itemToCompare.getAge().toString().equals(typedText));

然后,像下面的例子一样添加一个 StringConverter (因为 ComboBox 值将返回一个 String 并且它必须被转换成你的对象):

myComboBox.setConverter(new StringConverter<>() {

    @Override
    public String toString(YourObject object) {
        return object != null ? object.getName() : "";
    }

    @Override
    public YourObject fromString(String string) {
        return myComboBox.getItems().stream().filter(object ->
                object.getName().equals(string)).findFirst().orElse(null);
    }

});

当你需要从组合框中获取选中的值的时候也一定要使用这个方法,否则你可能会遇到一些异常,比如“class cast exception”:

FxUtilTest.getComboBoxValue(myComboBox);

P.S.:在 JRE 8.51 和 8.65 之间的版本中,此方法存在一些问题,导致一些奇怪的行为,现在问题似乎不再发生了。如果您遇到一些问题,您可以查看对此答案所做的编辑并获取当时解决问题的旧版本。 这种方法一定可以正常工作,如果您遇到任何问题,请告诉我。

关于java - JavaFX 中的自动完成组合框,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19924852/

相关文章:

Python-tkinter组合框保存值

python - Sublime Text 3 自动完成功能不起作用

ruby-on-rails - 没有得到 :id of element using AutoComplete jquery

java - 错误的类路径导致 java.lang.NoClassDefFoundError

java - 启动扩展 SherlockMapActivity 的 Activity 会引发 NoClassDefFoundError 异常

Java按字母数量排序

java - 无法建立安全连接,因为此站点使用不受支持的协议(protocol)

javascript - 在codeigniter中使用javascript将组合框添加到html

WPF 组合框控件模板背景 IsMouseOver

django - 如何实现干草堆搜索获取自动完成功能