当前位置: 首页 > 知识库问答 >
问题:

JavaFX-过滤组合框

韦原
2023-03-14

我想要一个组合框,它会在用户键入时过滤列表项。它应该如下工作:

  • 键入时,文本字段应该显示一个可能的选择,但是用户尚未键入的单词部分应该突出显示。
  • 当他打开列表时,下拉框应该只显示可能的选项?
  • 使用箭头键,用户应该在缩小可能的项目后选择剩余的项目之一。
  • 过滤并不重要,跳转到第一个匹配的选择也可以。

有类似的吗?

共有3个答案

郑狐若
2023-03-14

我搜索了一段时间,发现了这个。看看:

public class AutoCompleteComboBoxListener<T> implements EventHandler<KeyEvent> {

    private ComboBox comboBox;
    private StringBuilder sb;
    private ObservableList<T> data;
    private boolean moveCaretToPos = false;
    private int caretPos;

    public AutoCompleteComboBoxListener(final ComboBox comboBox) {
        this.comboBox = comboBox;
        sb = new StringBuilder();
        data = comboBox.getItems();

        this.comboBox.setEditable(true);
        this.comboBox.setOnKeyPressed(new EventHandler<KeyEvent>() {

            @Override
            public void handle(KeyEvent t) {
                comboBox.hide();
            }
        });
        this.comboBox.setOnKeyReleased(AutoCompleteComboBoxListener.this);
    }

    @Override
    public void handle(KeyEvent event) {
        ListView lv = ((ComboBoxListViewSkin) comboBox.getSkin()).getListView();

        if(event.getCode() == KeyCode.UP) {
            caretPos = -1;
            moveCaret(comboBox.getEditor().getText().length());
            return;
        } else if(event.getCode() == KeyCode.DOWN) {
            if(!comboBox.isShowing()) {
                comboBox.show();
            }
            caretPos = -1;
            moveCaret(comboBox.getEditor().getText().length());
            return;
        } else if(event.getCode() == KeyCode.BACK_SPACE) {
            moveCaretToPos = true;
            caretPos = comboBox.getEditor().getCaretPosition();
        } else if(event.getCode() == KeyCode.DELETE) {
            moveCaretToPos = true;
            caretPos = comboBox.getEditor().getCaretPosition();
        }

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

        ObservableList list = FXCollections.observableArrayList();
        for (int i=0; i<data.size(); i++) {
            if(data.get(i).toString().toLowerCase().startsWith(
                AutoCompleteComboBoxListener.this.comboBox
                .getEditor().getText().toLowerCase())) {
                list.add(data.get(i));
            }
        }
        String 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;
    }

}

你可以用

new AutoCompleteComboBoxListener<>(comboBox);

基于此,我根据自己的需要进行了定制。

请随意使用它,如果有人能改进它,告诉我。

丁韬
2023-03-14

看看:

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextField;

public class FilterComboBox extends ComboBox<String> {
    private ObservableList<String> initialList;
    private ObservableList<String> bufferList = FXCollections.observableArrayList();
    private String previousValue = "";

    public FilterComboBox(ObservableList<String> items) {
        super(items);
        super.setEditable(true);
        this.initialList = items;

        this.configAutoFilterListener();
    }

    private void configAutoFilterListener() {
        final FilterComboBox currentInstance = this;
        this.getEditor().textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
                previousValue = oldValue;
                final TextField editor = currentInstance.getEditor();
                final String selected = currentInstance.getSelectionModel().getSelectedItem();

                if (selected == null || !selected.equals(editor.getText())) {
                    filterItems(newValue, currentInstance);

                    currentInstance.show();
                    if (currentInstance.getItems().size() == 1) {
                        setUserInputToOnlyOption(currentInstance, editor);
                    }
                }
            }
        });
    }

    private void filterItems(String filter, ComboBox<String> comboBox) {
        if (filter.startsWith(previousValue) && !previousValue.isEmpty()) {
            ObservableList<String> filteredList = this.readFromList(filter, bufferList);
            bufferList.clear();
            bufferList = filteredList;
        } else {
            bufferList = this.readFromList(filter, initialList);
        }
        comboBox.setItems(bufferList);
    }

    private ObservableList<String> readFromList(String filter, ObservableList<String> originalList) {
        ObservableList<String> filteredList = FXCollections.observableArrayList();
        for (String item : originalList) {
            if (item.toLowerCase().startsWith(filter.toLowerCase())) {
                filteredList.add(item);
            }
        }

        return filteredList;
    }

    private void setUserInputToOnlyOption(ComboBox<String> currentInstance, final TextField editor) {
        final String onlyOption = currentInstance.getItems().get(0);
        final String currentText = editor.getText();
        if (onlyOption.length() > currentText.length()) {
            editor.setText(onlyOption);
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    editor.selectRange(currentText.length(), onlyOption.length());
                }
            });
        }
    }
}

这是基于在这个论坛上找到的答案。希望这有帮助。

笪栋
2023-03-14

就下拉列表的过滤而言。将可能的选项列表包装在FilteredList中不是最好的解决方案吗?

MCVE:

import javafx.application.Application;
import javafx.application.Platform;
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.control.TextField;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class MCVE extends Application {
    public void start(Stage stage) {
        HBox root = new HBox();

        ComboBox<String> cb = new ComboBox<String>();
        cb.setEditable(true);

        // Create a list with some dummy values.
        ObservableList<String> items = FXCollections.observableArrayList("One", "Two", "Three", "Four", "Five", "Six",
                "Seven", "Eight", "Nine", "Ten");

        // Create a FilteredList wrapping the ObservableList.
        FilteredList<String> filteredItems = new FilteredList<String>(items, p -> true);

        // Add a listener to the textProperty of the combobox editor. The
        // listener will simply filter the list every time the input is changed
        // as long as the user hasn't selected an item in the list.
        cb.getEditor().textProperty().addListener((obs, oldValue, newValue) -> {
            final TextField editor = cb.getEditor();
            final String selected = cb.getSelectionModel().getSelectedItem();

            // This needs run on the GUI thread to avoid the error described
            // here: https://bugs.openjdk.java.net/browse/JDK-8081700.
            Platform.runLater(() -> {
                // If the no item in the list is selected or the selected item
                // isn't equal to the current input, we refilter the list.
                if (selected == null || !selected.equals(editor.getText())) {
                    filteredItems.setPredicate(item -> {
                        // We return true for any items that starts with the
                        // same letters as the input. We use toUpperCase to
                        // avoid case sensitivity.
                        if (item.toUpperCase().startsWith(newValue.toUpperCase())) {
                            return true;
                        } else {
                            return false;
                        }
                    });
                }
            });
        });

        cb.setItems(filteredItems);

        root.getChildren().add(cb);

        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}
 类似资料:
  • 我有一个简单的可编辑的JComboBox,当您键入时,它过滤可用的选择。除了一些奇怪的情况外,它几乎可以工作,例如,当你键入“Sass”时,它将可用的选择过滤到一个“Sassi di Matera”,但如果你选择它,它将选择“Arte ruprestre della Vacamonica”,它碰巧是原始模型的第[0]项,而不是过滤后的模型的第[0]项。 我试着调试了几个小时,但似乎是一些奇怪的up

  • 我需要关于设置组合框按钮单元格的帮助。我使用一个组合框来显示可观察列表中的数据,该列表包含两个列的表中的数据,“步骤”和“下一步”(下一步包含一个插入在步骤列中的项目);我需要做的是显示带有“步骤”列表的组合框列表单元格和相对的“下一步”按钮单元格。现在,我可以正确地看到列表单元格,但我的按钮单元格总是空的。 代码: 提前感谢。

  • 问题内容: 我正在使用ES 1.7,试图使用完全匹配字符串来搜索文档。单独使用时,过滤器可以正常工作,但是当我组合使用过滤器时,会出现错误。 例如:人员文档 -通过姓名和地址搜索人员,效果很好。 -通过姓名和电话号码搜索人也可以。 但是,当我尝试同时使用过滤器,地址和电话时,出现错误 例如: 一起使用和过滤器时出现错误: 索引映射(人): 我用来测试的文件 问题答案: 最终,我能够重现该问题,因为

  • 问题内容: 我正在尝试对数组中的值进行聚合,并且还过滤由前缀返回的存储桶。不知道这是否可行,或者我滥用过滤桶。 3份文件: 目的是获取带有字母B开头颜色的文档数量: 不幸的是,返回的结果包括Red。显然是因为带有红色的文档仍然按过滤器匹配,因为它们也具有蓝色和/或黑色。 有没有一种方法可以只过滤存储桶结果? 问题答案: 尝试此操作,它将过滤为存储桶本身创建的值:

  • 有没有办法把这两条流合并成一条?我使用第一个流在嵌套列表中进行过滤和查找,并使用第二个流根据流的结果创建地图。我想知道是否有一种方法可以用一条流来实现这一点。 像这样的

  • 提前谢了。