I stumbled on (in my eyes) a silly problem. However I don't find a solution for this (maybe because of not using the right search keywords, or by making it too difficult when it can be easy..) Scenario:
I have a combobox with 500 customers. I have to select a single costumer.
In Swing, when the list was down and you started typing, it automatically jumps to the typed letter. E.g.:
Items:
When the combobox list is open, I just type 'R' and, in swing, it jumped to the first customer starting with 'R'. In javafx 2 it seems it does not have that behaviour... Is there some option that I have to enable or should I do something like using an editable combobox instead and make a filter()
method that is fired on every keypress?
Edit: sollution based on Bhupendra's answer:
public class FilterComboBox<T> extends ComboBox<T> {
private final FilterComboBox<T> fcbo = this;
//private FilterComboBox fcbo = this;
private ObservableList<T> items;
private ObservableList<T> filter;
private String s;
private Object selection;
private class KeyHandler implements EventHandler< KeyEvent> {
private SingleSelectionModel<T> sm;
public KeyHandler() {
sm = getSelectionModel();
s = "";
}
@Override
public void handle(KeyEvent event) {
filter.clear();
// handle non alphanumeric keys like backspace, delete etc
if (event.getCode() == KeyCode.BACK_SPACE && s.length() > 0) {
s = s.substring(0, s.length() - 1);
} else {
s += event.getText();
}
if (s.length() == 0) {
fcbo.setItems(items);
sm.selectFirst();
return;
}
//System.out.println(s);
if (event.getCode().isLetterKey()) {
for (T item : items) {
if (item.toString().toUpperCase().startsWith(s.toUpperCase())) {
filter.add(item);
//System.out.println(item);
fcbo.setItems(filter);
//sm.clearSelection();
//sm.select(item);
}
}
sm.select(0);
}
}
}
public FilterComboBox(final ObservableList<T> items) {
super(items);
this.items = items;
this.filter = FXCollections.observableArrayList();
setOnKeyReleased(new KeyHandler());
this.focusedProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
if (newValue == false) {
s = "";
fcbo.setItems(items);
fcbo.getSelectionModel().select((T)selection);
}
}
});
this.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
if (newValue != null) {
selection = (Object) newValue;
}
}
});
}
}
The simplest form of a filter combo box would be as the code below. But it would need more work to refine it. Also, if the list is huge, as in your case, there might be a performance issues as we are looping thru' the entire collection on each key press.
public class FilterComboBox extends ComboBox< String > {
private ObservableList< String > items;
private class KeyHandler implements EventHandler< KeyEvent > {
private SingleSelectionModel< String > sm;
private String s;
public KeyHandler() {
sm = getSelectionModel();
s = "";
}
@Override
public void handle( KeyEvent event ) {
// handle non alphanumeric keys like backspace, delete etc
if( event.getCode() == KeyCode.BACK_SPACE && s.length()>0)
s = s.substring( 0, s.length() - 1 );
else s += event.getText();
if( s.length() == 0 ) {
sm.selectFirst();
return;
}
System.out.println( s );
for( String item: items ) {
if( item.startsWith( s ) ) sm.select( item );
}
}
}
public FilterComboBox( ObservableList< String > items ) {
super( items );
this.items = items;
setOnKeyReleased( new KeyHandler() );
}
}
Wouldn't code like this be sufficient?
comboBox.setOnKeyReleased(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {
String s = jumpTo(event.getText(), comboBox.getValue(), comboBox.getItems());
if (s != null) {
comboBox.setValue(s);
}
}
});
...
static String jumpTo(String keyPressed, String currentlySelected, List<String> items) {
String key = keyPressed.toUpperCase();
if (key.matches("^[A-Z]$")) {
// Only act on letters so that navigating with cursor keys does not
// try to jump somewhere.
boolean letterFound = false;
boolean foundCurrent = currentlySelected == null;
for (String s : items) {
if (s.toUpperCase().startsWith(key)) {
letterFound = true;
if (foundCurrent) {
return s;
}
foundCurrent = s.equals(currentlySelected);
}
}
if (letterFound) {
return jumpTo(keyPressed, null, items);
}
}
return null;
}
This will jump to the first item when you press a letter. If you press that letter again, it jumps to the next item starting with that letter, wrapping back to the first if there are no more items starting with that letter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With