我正在寻找一种向JavaFX ComboBox
添加自动完成功能的方法。
经过大量的搜索,现在可以在这里询问。
此AutoFillBox
已知,但不是我正在搜索的内容。
我想要的是一个可编辑的组合框,输入列表时应该过滤掉。但我也希望打开列表而不打字并查看整个项目。
有什么想法吗?
答案 0 :(得分:53)
首先,您必须在项目中创建此类:
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());
}
}
}
要使您的组合框自动完成,请按以下方式使用:
FxUtilTest.autoCompleteComboBoxPlus(myComboBox, (typedText, itemToCompare) -> itemToCompare.getName().toLowerCase().contains(typedText.toLowerCase()) || itemToCompare.getAge().toString().equals(typedText));
当您需要从组合框中获取所选值时,请务必使用此方法,否则您可能会遇到一些例外情况,例如“类强制转换异常”:
FxUtilTest.getComboBoxValue(myComboBox);
P.S。:这个方法在jre 8.51和8.65之间的版本中存在一些问题,导致一些奇怪的行为,现在问题似乎不再发生了。如果您遇到某些问题,可以看到对此答案所做的编辑,并获取当时修复问题的旧版本。 这个方法必须正常,如果您遇到任何问题,请告诉我。
答案 1 :(得分:36)
我找到了一个适合我的解决方案:
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) {
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);
它基于this,我根据自己的需要对其进行了定制。
随意使用它,如果有人可以改进它,请告诉我。
答案 2 :(得分:13)
使用ControlsFX库,您可以使用两行代码执行此操作:
<!doctype html>
<html>
<head>
<title></title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body ng-app="app" ng-controller="WelcomeController as self">
<div class="container">
<form name="form" class="col-md-4">
<div class="form-group">
<input class="form-control" ng-model="self.account.firstName" placeholder="firstName" ng-required="true" />
<input class="form-control" ng-model="self.account.lastName" placeholder="lastName" ng-required="true" />
<input class="form-control" ng-model="self.account.street" placeholder="street" ng-required="true" />
<label class="radio-inline">
<input name="i-account" type="radio" ng-model="self.account.gender" ng-value="1" ng-required="true">
<span class="fa fa-circle"></span> Male
</label>
<label class="radio-inline">
<input name="i-account" type="radio" ng-model="self.account.gender" ng-value="2" ng-required="true">
<span class="fa fa-circle"></span> Female
</label>
</div>
<div class="form-group">
<input class="form-control" ng-model="self.staff.firstName" placeholder="firstName" />
<input class="form-control" ng-model="self.staff.lastName" placeholder="lastName" />
<label class="radio-inline">
<input name="i-staff" type="radio" ng-model="self.staff.gender" ng-value="1" ng-required="true">
<span class="fa fa-circle"></span> Male
</label>
<label class="radio-inline">
<input name="i-staff" type="radio" ng-model="self.staff.gender" ng-value="2" ng-checked="true" ng-required="true">
<span class="fa fa-circle"></span> Female
</label>
</div>
<button class="btn btn-primary" ng-click="self.prvStep()">Previous</button>
<button class="btn btn-success" ng-click="self.saveStep()" ng-disabled="formStep2.$invalid">finish</button>
</form>
</div>
</body>
</html>
答案 3 :(得分:7)
基于Jonatan's answer,我能够构建以下解决方案:
import com.sun.javafx.scene.control.skin.ComboBoxListViewSkin;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.property.*;
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.Control;
import javafx.scene.control.ListView;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
public class Main extends Application
{
public static class HideableItem<T>
{
private final ObjectProperty<T> object = new SimpleObjectProperty<>();
private final BooleanProperty hidden = new SimpleBooleanProperty();
private HideableItem(T object)
{
setObject(object);
}
private ObjectProperty<T> objectProperty(){return this.object;}
private T getObject(){return this.objectProperty().get();}
private void setObject(T object){this.objectProperty().set(object);}
private BooleanProperty hiddenProperty(){return this.hidden;}
private boolean isHidden(){return this.hiddenProperty().get();}
private void setHidden(boolean hidden){this.hiddenProperty().set(hidden);}
@Override
public String toString()
{
return getObject() == null ? null : getObject().toString();
}
}
public void start(Stage stage)
{
List<String> countries = new ArrayList<>();
countries.add("Afghanistan");
countries.add("Albania");
countries.add("Algeria");
countries.add("Andorra");
countries.add("Angola");
countries.add("Antigua and Barbuda");
countries.add("Argentina");
countries.add("Armenia");
countries.add("Australia");
countries.add("Austria");
countries.add("Azerbaijan");
countries.add("Bahamas");
countries.add("Bahrain");
countries.add("Bangladesh");
countries.add("Barbados");
countries.add("Belarus");
countries.add("Belgium");
countries.add("Belize");
countries.add("Benin");
countries.add("Bhutan");
countries.add("Bolivia");
countries.add("Bosnia and Herzegovina");
countries.add("Botswana");
countries.add("Brazil");
countries.add("Brunei");
countries.add("Bulgaria");
countries.add("Burkina Faso");
countries.add("Burundi");
countries.add("Cabo Verde");
countries.add("Cambodia");
countries.add("Cameroon");
countries.add("Canada");
countries.add("Central African Republic (CAR)");
countries.add("Chad");
countries.add("Chile");
countries.add("China");
countries.add("Colombia");
countries.add("Comoros");
countries.add("Democratic Republic of the Congo");
countries.add("Republic of the Congo");
countries.add("Costa Rica");
countries.add("Cote d'Ivoire");
countries.add("Croatia");
countries.add("Cuba");
countries.add("Cyprus");
countries.add("Czech Republic");
countries.add("Denmark");
countries.add("Djibouti");
countries.add("Dominica");
countries.add("Dominican Republic");
countries.add("Ecuador");
countries.add("Egypt");
countries.add("El Salvador");
countries.add("Equatorial Guinea");
countries.add("Eritrea");
countries.add("Estonia");
countries.add("Ethiopia");
countries.add("Fiji");
countries.add("Finland");
countries.add("France");
countries.add("Gabon");
countries.add("Gambia");
countries.add("Georgia");
countries.add("Germany");
countries.add("Ghana");
countries.add("Greece");
countries.add("Grenada");
countries.add("Guatemala");
countries.add("Guinea");
countries.add("Guinea-Bissau");
countries.add("Guyana");
countries.add("Haiti");
countries.add("Honduras");
countries.add("Hungary");
countries.add("Iceland");
countries.add("India");
countries.add("Indonesia");
countries.add("Iran");
countries.add("Iraq");
countries.add("Ireland");
countries.add("Israel");
countries.add("Italy");
countries.add("Jamaica");
countries.add("Japan");
countries.add("Jordan");
countries.add("Kazakhstan");
countries.add("Kenya");
countries.add("Kiribati");
countries.add("Kosovo");
countries.add("Kuwait");
countries.add("Kyrgyzstan");
countries.add("Laos");
countries.add("Latvia");
countries.add("Lebanon");
countries.add("Lesotho");
countries.add("Liberia");
countries.add("Libya");
countries.add("Liechtenstein");
countries.add("Lithuania");
countries.add("Luxembourg");
countries.add("Macedonia (FYROM)");
countries.add("Madagascar");
countries.add("Malawi");
countries.add("Malaysia");
countries.add("Maldives");
countries.add("Mali");
countries.add("Malta");
countries.add("Marshall Islands");
countries.add("Mauritania");
countries.add("Mauritius");
countries.add("Mexico");
countries.add("Micronesia");
countries.add("Moldova");
countries.add("Monaco");
countries.add("Mongolia");
countries.add("Montenegro");
countries.add("Morocco");
countries.add("Mozambique");
countries.add("Myanmar (Burma)");
countries.add("Namibia");
countries.add("Nauru");
countries.add("Nepal");
countries.add("Netherlands");
countries.add("New Zealand");
countries.add("Nicaragua");
countries.add("Niger");
countries.add("Nigeria");
countries.add("North Korea");
countries.add("Norway");
countries.add("Oman");
countries.add("Pakistan");
countries.add("Palau");
countries.add("Palestine");
countries.add("Panama");
countries.add("Papua New Guinea");
countries.add("Paraguay");
countries.add("Peru");
countries.add("Philippines");
countries.add("Poland");
countries.add("Portugal");
countries.add("Qatar");
countries.add("Romania");
countries.add("Russia");
countries.add("Rwanda");
countries.add("Saint Kitts and Nevis");
countries.add("Saint Lucia");
countries.add("Saint Vincent and the Grenadines");
countries.add("Samoa");
countries.add("San Marino");
countries.add("Sao Tome and Principe");
countries.add("Saudi Arabia");
countries.add("Senegal");
countries.add("Serbia");
countries.add("Seychelles");
countries.add("Sierra Leone");
countries.add("Singapore");
countries.add("Slovakia");
countries.add("Slovenia");
countries.add("Solomon Islands");
countries.add("Somalia");
countries.add("South Africa");
countries.add("South Korea");
countries.add("South Sudan");
countries.add("Spain");
countries.add("Sri Lanka");
countries.add("Sudan");
countries.add("Suriname");
countries.add("Swaziland");
countries.add("Sweden");
countries.add("Switzerland");
countries.add("Syria");
countries.add("Taiwan");
countries.add("Tajikistan");
countries.add("Tanzania");
countries.add("Thailand");
countries.add("Timor-Leste");
countries.add("Togo");
countries.add("Tonga");
countries.add("Trinidad and Tobago");
countries.add("Tunisia");
countries.add("Turkey");
countries.add("Turkmenistan");
countries.add("Tuvalu");
countries.add("Uganda");
countries.add("Ukraine");
countries.add("United Arab Emirates (UAE)");
countries.add("United Kingdom (UK)");
countries.add("United States of America (USA)");
countries.add("Uruguay");
countries.add("Uzbekistan");
countries.add("Vanuatu");
countries.add("Vatican City (Holy See)");
countries.add("Venezuela");
countries.add("Vietnam");
countries.add("Yemen");
countries.add("Zambia");
countries.add("Zimbabwe");
ComboBox<HideableItem<String>> comboBox = createComboBoxWithAutoCompletionSupport(countries);
comboBox.setMaxWidth(Double.MAX_VALUE);
HBox root = new HBox();
root.getChildren().add(comboBox);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
comboBox.setMinWidth(comboBox.getWidth());
comboBox.setPrefWidth(comboBox.getWidth());
}
public static void main(String[] args)
{
launch();
}
private static <T> ComboBox<HideableItem<T>> createComboBoxWithAutoCompletionSupport(List<T> items)
{
ObservableList<HideableItem<T>> hideableHideableItems = FXCollections.observableArrayList(hideableItem -> new Observable[]{hideableItem.hiddenProperty()});
items.forEach(item ->
{
HideableItem<T> hideableItem = new HideableItem<>(item);
hideableHideableItems.add(hideableItem);
});
FilteredList<HideableItem<T>> filteredHideableItems = new FilteredList<>(hideableHideableItems, t -> !t.isHidden());
ComboBox<HideableItem<T>> comboBox = new ComboBox<>();
comboBox.setItems(filteredHideableItems);
@SuppressWarnings("unchecked")
HideableItem<T>[] selectedItem = (HideableItem<T>[]) new HideableItem[1];
comboBox.addEventHandler(KeyEvent.KEY_PRESSED, event ->
{
if(!comboBox.isShowing()) return;
comboBox.setEditable(true);
comboBox.getEditor().clear();
});
comboBox.showingProperty().addListener((observable, oldValue, newValue) ->
{
if(newValue)
{
@SuppressWarnings("unchecked")
ListView<HideableItem> lv = ((ComboBoxListViewSkin<HideableItem>) comboBox.getSkin()).getListView();
Platform.runLater(() ->
{
if(selectedItem[0] == null) // first use
{
double cellHeight = ((Control) lv.lookup(".list-cell")).getHeight();
lv.setFixedCellSize(cellHeight);
}
});
lv.scrollTo(comboBox.getValue());
}
else
{
HideableItem<T> value = comboBox.getValue();
if(value != null) selectedItem[0] = value;
comboBox.setEditable(false);
Platform.runLater(() ->
{
comboBox.getSelectionModel().select(selectedItem[0]);
comboBox.setValue(selectedItem[0]);
});
}
});
comboBox.setOnHidden(event -> hideableHideableItems.forEach(item -> item.setHidden(false)));
comboBox.getEditor().textProperty().addListener((obs, oldValue, newValue) ->
{
if(!comboBox.isShowing()) return;
Platform.runLater(() ->
{
if(comboBox.getSelectionModel().getSelectedItem() == null)
{
hideableHideableItems.forEach(item -> item.setHidden(!item.getObject().toString().toLowerCase().contains(newValue.toLowerCase())));
}
else
{
boolean validText = false;
for(HideableItem hideableItem : hideableHideableItems)
{
if(hideableItem.getObject().toString().equals(newValue))
{
validText = true;
break;
}
}
if(!validText) comboBox.getSelectionModel().select(null);
}
});
});
return comboBox;
}
}
在Java 9+中,您可以像这样访问ListView
:
ListView<ComboBoxItem> lv = (ListView<ComboBoxItem>) ((ComboBoxListViewSkin<?>) comboBox.getSkin()).getPopupContent();
答案 4 :(得分:4)
我环顾四周尝试一下。 这看起来不错:
public void handle( KeyEvent event ) {
if( event.getCode() == KeyCode.BACK_SPACE)
s = s.substring( 0, s.length() - 1 );
else s += event.getText();
for( String item: items ) {
if( item.startsWith( s ) ) sm.select( item );
}
}
用于选择具有匹配的开始字符的项目的键控。
我希望这可以帮到你
答案 5 :(得分:2)
我发现了英·富阿德的 answer成为最佳(甚至超过10,000个项目),但我不得不修复3个错误:
1)单击SHIFT
时,编辑器消失了
2)键入SPACE
时,ComboBox
将关闭
3)当“清除选择”然后打开组合框并关闭而不选择任何内容时,它将重新选择最后一项。
我还添加了一个StringConverter
,使用了Apache StringUtils进行比较,并将流移动到常规的for循环中,以实现如下性能目的:https://blog.jooq.org/2015/12/08/3-reasons-why-you-shouldnt-replace-your-for-loops-by-stream-foreach/
import com.sun.javafx.scene.control.skin.ComboBoxListViewSkin;
import java.util.List;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.Event;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Control;
import javafx.scene.control.ListView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.util.StringConverter;
import org.apache.commons.lang3.StringUtils;
public class AutoCompleteComboBox
{
public static class HideableItem<T>
{
private final ObjectProperty<T> object = new SimpleObjectProperty<>();
private final BooleanProperty hidden = new SimpleBooleanProperty();
private StringConverter converter;
public HideableItem(T object, StringConverter converter)
{
setConverter(converter);
setObject(object);
}
private ObjectProperty<T> objectProperty()
{
return this.object;
}
public T getObject()
{
return this.objectProperty().get();
}
private void setObject(T object)
{
this.objectProperty().set(object);
}
private BooleanProperty hiddenProperty()
{
return this.hidden;
}
private boolean isHidden()
{
return this.hiddenProperty().get();
}
private void setHidden(boolean hidden)
{
this.hiddenProperty().set(hidden);
}
private void setConverter(StringConverter converter)
{
this.converter = converter;
}
@Override
public String toString()
{
return getObject() == null ? null : converter.toString(this);
}
}
public static <T> ComboBox<HideableItem<T>> createComboBoxWithAutoCompletionSupport(List<T> items, StringConverter converter)
{
ObservableList<HideableItem<T>> hideableHideableItems = FXCollections.observableArrayList(hideableItem -> new Observable[]
{
hideableItem.hiddenProperty()
});
for (T item : items)
{
HideableItem<T> hideableItem = new HideableItem<>(item, converter);
hideableHideableItems.add(hideableItem);
}
FilteredList<HideableItem<T>> filteredHideableItems = new FilteredList<>(hideableHideableItems, t -> !t.isHidden());
ComboBox<HideableItem<T>> comboBox = new ComboBox<>();
comboBox.setItems(filteredHideableItems);
comboBox.setConverter(converter);
ComboBoxListViewSkin<HideableItem<T>> comboBoxListViewSkin = new ComboBoxListViewSkin<HideableItem<T>>(comboBox);
comboBoxListViewSkin.getPopupContent().addEventFilter(KeyEvent.ANY, (KeyEvent event) ->
{
if (event.getCode() == KeyCode.SPACE)
{
event.consume();
}
});
comboBox.setSkin(comboBoxListViewSkin);
@SuppressWarnings("unchecked")
HideableItem<T>[] selectedItem = (HideableItem<T>[]) new HideableItem[1];
comboBox.addEventHandler(KeyEvent.KEY_PRESSED, event ->
{
if (!comboBox.isShowing() || event.isShiftDown() || event.isControlDown())
{
return;
}
comboBox.setEditable(true);
comboBox.getEditor().clear();
});
comboBox.showingProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) ->
{
if (newValue)
{
@SuppressWarnings("unchecked")
ListView<HideableItem> lv = ((ComboBoxListViewSkin<HideableItem>) comboBox.getSkin()).getListView();
Platform.runLater(() ->
{
if (selectedItem[0] == null) // first use
{
double cellHeight = ((Control) lv.lookup(".list-cell")).getHeight();
lv.setFixedCellSize(cellHeight);
}
});
lv.scrollTo(comboBox.getValue());
} else
{
HideableItem<T> value = comboBox.getValue();
if (value != null)
{
selectedItem[0] = value;
}
comboBox.setEditable(false);
if (value != null)
{
Platform.runLater(() ->
{
comboBox.getSelectionModel().select(selectedItem[0]);
comboBox.setValue(selectedItem[0]);
});
}
}
});
comboBox.setOnHidden((Event event) ->
{
for (HideableItem item : hideableHideableItems)
{
item.setHidden(false);
}
});
comboBox.valueProperty().addListener((ObservableValue<? extends HideableItem<T>> obs, HideableItem<T> oldValue, HideableItem<T> newValue) ->
{
if (newValue == null)
{
for (HideableItem item : hideableHideableItems)
{
item.setHidden(false);
}
}
});
comboBox.getEditor().textProperty().addListener((ObservableValue<? extends String> obs, String oldValue, String newValue) ->
{
if (!comboBox.isShowing())
{
return;
}
Platform.runLater(() ->
{
if (comboBox.getSelectionModel().getSelectedItem() == null)
{
for (HideableItem item : hideableHideableItems)
{
item.setHidden(!StringUtils.containsIgnoreCase(item.toString(), newValue));
}
} else
{
boolean validText = false;
for (HideableItem hideableItem : hideableHideableItems)
{
if (hideableItem.toString().equals(newValue))
{
validText = true;
break;
}
}
if (!validText)
{
comboBox.getSelectionModel().select(null);
}
}
});
});
return comboBox;
}
}
答案 6 :(得分:1)
添加到Mateus&#39;代码,以下将为自动完成创建提示文本。例如,如果您键入&#34; s&#34;,则以&#34; s&#34;开头的项目来自ObservableArray(填充ComboBox)将作为提示文本。显然,将它用于&#34;包含&#34;并没有多大意义。参数。
public class ACComboBox1 {
static String some;
static String typedText;
static StringBuilder sb = new StringBuilder();
public enum AutoCompleteMode {
STARTS_WITH,CONTAINING,;
}
public static<T> void autoCompleteComboBox(ComboBox<T> comboBox, AutoCompleteMode mode) {
ObservableList<T> data = comboBox.getItems();
comboBox.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
public void handle(KeyEvent event){
comboBox.hide();
}
});
comboBox.addEventHandler(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() {
private boolean moveCaretToPos = false;
private int caretPos;
public void handle(KeyEvent event) {
String keyPressed = event.getCode().toString().toLowerCase();
if ("space".equals(keyPressed) ){
typedText= " ";
} else if ("shift".equals(keyPressed ) || "command".equals(keyPressed)
|| "alt".equals(keyPressed) ) {
return;
} else {
typedText = event.getCode().toString().toLowerCase();
}
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();
typedText=null;
sb.delete(0, sb.length());
comboBox.getEditor().setText(null);
return;
} else if (event.getCode() == KeyCode.DELETE) {
moveCaretToPos = true;
caretPos = comboBox.getEditor().getCaretPosition();
return;
} else if (event.getCode().equals(KeyCode.TAB)) {
some = null;
typedText = null;
sb.delete(0, sb.length());
return;
} else if (event.getCode() == KeyCode.LEFT
|| event.isControlDown() || event.getCode() == KeyCode.HOME
|| event.getCode() == KeyCode.END || event.getCode() == KeyCode.RIGHT) {
return;
}
if (typedText==null){
typedText = comboBox.getEditor().getText().toLowerCase();
sb.append(typedText);
}else{
System.out.println("sb:"+sb);
System.out.println("tt:"+typedText);
sb.append(typedText);
}
ObservableList<T> list = FXCollections.observableArrayList();
for (T aData : data) {
if (mode.equals(AutoCompleteMode.STARTS_WITH) && aData.toString().toLowerCase().startsWith(sb.toString())) {
list.add(aData);
some = aData.toString();
} else if (mode.equals(AutoCompleteMode.CONTAINING) && aData.toString().toLowerCase().contains(comboBox.getEditor().getText().toLowerCase())) {
list.add(aData);
}
}
comboBox.setItems(list);
comboBox.getEditor().setText(some);
comboBox.getEditor().positionCaret(sb.length());
comboBox.getEditor().selectEnd();
if (!moveCaretToPos) {
caretPos = -1;
}
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());
}
}
}
答案 7 :(得分:1)
这是一个简单的
public class AutoShowComboBoxHelper {
public AutoShowComboBoxHelper(final ComboBox<String> comboBox, final Callback<String, String> textBuilder) {
final ObservableList<String> items = FXCollections.observableArrayList(comboBox.getItems());
comboBox.getEditor().textProperty().addListener((ov, o, n) -> {
if (n.equals(comboBox.getSelectionModel().getSelectedItem())) {
return;
}
comboBox.hide();
final FilteredList<String> filtered = items.filtered(s -> textBuilder.call(s).toLowerCase().contains(n.toLowerCase()));
if (filtered.isEmpty()) {
comboBox.getItems().setAll(items);
} else {
comboBox.getItems().setAll(filtered);
comboBox.show();
}
});
}
}
以及使用它的方法:
new AutoShowComboBoxHelper(combo, item -> buildTextToCompare(item));
为了简单起见,我在本代码中使用了String :: contains,以便更好地使用org.apache.commons.lang3.StringUtils :: containsIgnoreCase
答案 8 :(得分:1)
不知道这是否仍然相关,而是来到这个主题。尝试了上层方法,我自己做了一个变化。但到目前为止,这个fxapps blog一个看起来最好看(但由于我的项目列表不是静态的,但仍然需要进行一些修改,但仍然......)。希望这将使下一个会发现这个主题的人感兴趣。
答案 9 :(得分:1)
参加聚会有点晚了,但是当我碰到了这个问题并且真的很喜欢答案和方法时,我碰到了这个话题,所以感谢所有贡献者。
但是,我不知道你们在ComboBox
中放了什么,每当我离开框而没有做出特定选择时,都会抛出ClassCastException
。因此,我想大家都主要使用ComboBox
来选择字符串,所以我不得不提出一个StringConverter,因为我将ComboBox
用于对象(FilterCriteria)。
这就是转换器,希望对某人有所帮助。
private final StringConverter<FilterCriteria> filterCriteriaStringConverter = new StringConverter<FilterCriteria>()
{
@Override public String toString(FilterCriteria filterCriteria)
{
if (filterCriteria == null)
{
return "";
} else
{
return filterCriteria.getName();
}
}
@Override public FilterCriteria fromString(String string)
{
Optional<FilterCriteria> optionalFilterCriteria = availableTypesComboBox.getItems().stream()
.filter(filterCriteria -> filterCriteria.getName().contains(string))
.findFirst();
return optionalFilterCriteria.orElseGet(() -> availableTypesComboBox.getItems().get(0));
}
};
答案 10 :(得分:0)
我建议尝试使用小型实用程序库jalvafx
中的解决方案List<String> items = Arrays.asList("Mercury",
"Venus",
"Earth",
"Mars",
"Jupiter",
"Saturn",
"Neptune");
ComboBoxCustomizer.create(comboBox)
.autocompleted(items)
.customize();
默认情况下,双击以清除值。还有一些其他有用的功能。您可以添加额外的列或字形,选择特定项目,将项目默认更改为字符串表示形式...
ComboBoxCustomizer.create(comboBox)
.autocompleted(items)
.overrideToString(o -> "planet: " + o)
.multyColumn(o -> Arrays.asList("column 2", "column 3"))
.emphasized(o -> o.endsWith("s"))
.customize();
答案 11 :(得分:0)
我找到了一个过滤组合框的简单解决方案,它不使用可编辑的组合框,但是它的确非常好,并且不会产生错误:http://fxapps.blogspot.com/2016/03/simplest-javafx-combobox-autocomplete.html
注意:请使用发布在该网站第一条评论上的修复程序。