JavaFX-过滤组合框

JavaFX-过滤组合框,javafx,combobox,filtering,Javafx,Combobox,Filtering,我想要一个组合框,当用户键入时过滤列表项。其工作应如下: 键入时,文本字段应显示一个可能的选择,但应突出显示用户尚未键入的部分 当他打开列表时,下拉菜单应该只显示可能的选项 在缩小可能的项目后,用户应使用箭头键选择剩余项目之一 过滤并不是那么重要,跳转到第一个匹配选择也可以 有类似的东西吗?我在类似的地方搜索了一会儿,发现了这个。 看一看: public class AutoCompleteComboBoxListener<T> implements EventHandler&l

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

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

有类似的东西吗?

我在类似的地方搜索了一会儿,发现了这个。 看一看:

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;
    }

}
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());
                }
            });
        }
    }
}
公共类AutoCompleteComboxListener实现EventHandler{
专用组合框组合框;
私家侦探;
私有可观测列表数据;
私有布尔值moveCaretToPos=false;
私人int caretPos;
公共自动完成组合框侦听器(最终组合框组合框){
this.comboBox=组合框;
sb=新的StringBuilder();
data=comboBox.getItems();
this.comboBox.setEditable(true);
this.comboBox.setOnKeyPressed(新的EventHandler(){
@凌驾
公共无效句柄(KeyEvent t){
comboBox.hide();
}
});
this.comboBox.setOnKeyReleased(autoCompleteComboxListener.this);
}
@凌驾
公共无效句柄(KeyEvent事件){
ListView lv=((ComboBoxListViewSkin)comboBox.getSkin()).getListView();
if(event.getCode()==KeyCode.UP){
caretPos=-1;
moveCaret(comboBox.getEditor().getText().length());
返回;
}else if(event.getCode()==KeyCode.DOWN){
如果(!comboBox.isShowing()){
comboBox.show();
}
caretPos=-1;
moveCaret(comboBox.getEditor().getText().length());
返回;
}else if(event.getCode()==KeyCode.BACK\u空格){
MoveCareTopos=真;
caretPos=comboBox.getEditor().getCaretPosition();
}else if(event.getCode()==KeyCode.DELETE){
MoveCareTopos=真;
caretPos=comboBox.getEditor().getCaretPosition();
}
如果(event.getCode()==KeyCode.RIGHT | | event.getCode()==KeyCode.LEFT
||event.isControlDown()| | event.getCode()==KeyCode.HOME
||event.getCode()==KeyCode.END | | event.getCode()==KeyCode.TAB){
返回;
}
ObservableList=FXCollections.observableArrayList();
对于(int i=0;i请看:

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;
    }

}
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());
                }
            });
        }
    }
}
导入javafx.application.Platform;
导入javafx.beans.value.ChangeListener;
导入javafx.beans.value.observeValue;
导入javafx.collections.FXCollections;
导入javafx.collections.ObservableList;
导入javafx.scene.control.ComboBox;
导入javafx.scene.control.TextField;
公共类筛选器ComboBox扩展组合框{
私人观察者初始列表;
私有ObservableList bufferList=FXCollections.observableArrayList();
私有字符串previousValue=“”;
公共过滤器通讯箱(可观察列表项){
超级(项目);
super.setEditable(true);
this.initialList=项目;
this.configAutoFilterListener();
}
私有void configAutoFilterListener(){
final FilterComboBox currentInstance=此;
this.getEditor().textProperty().addListener(新的ChangeListener()){
@凌驾

就下拉列表的筛选而言,public void已更改(ObservableValue。将可能选项列表包装在
FilteredList
中不是最佳解决方案吗

MCVE:

导入javafx.application.application;
导入javafx.application.Platform;
导入javafx.collections.FXCollections;
导入javafx.collections.ObservableList;
导入javafx.collections.transformation.FilteredList;
导入javafx.scene.scene;
导入javafx.scene.control.ComboBox;
导入javafx.scene.control.TextField;
导入javafx.scene.layout.HBox;
导入javafx.stage.stage;
公共类MCVE扩展应用程序{
公众假期开始(阶段){
HBox根=新的HBox();
ComboBox cb=新ComboBox();
cb.setEditable(true);
//创建一个包含一些虚拟值的列表。
ObservableList items=FXCollections.observableArrayList(“一”、“二”、“三”、“四”、“五”、“六”,
“七”、“八”、“九”、“十”);
//创建一个包装ObservableList的FilteredList。
FilteredList filteredItems=新的FilteredList(项目,p->true);
//将侦听器添加到组合框编辑器的textProperty
//每次更改输入时,侦听器都会简单地过滤列表
//只要用户没有选择列表中的项目。
cb.getEditor().textProperty().addListener((obs、oldValue、newValue)->{
final TextField editor=cb.getEditor();
最后选择的字符串=cb.getSelectionModel().getSelectedItem();
//这需要在GUI线程上运行,以避免所描述的错误
//在这里:https://bugs.openjdk.java.net/browse/JDK-8081700.
Platform.runLater(()->{
//如果未选择列表中的任何项目或选定的项目
//不等于当前输入,我们将重新筛选列表。
if(selected==null | |!selected.equals(editor.getText())){
filteredItems.setPredicate(项->{
//对于以
//与输入相同的字母。我们使用toUpperCase
//避免区分大小写。
if(item.toUpperCase().startsWith(newValue.toUpperCase())){
返回true;
}否则{
返回false;