seleccionar - javafx combobox setconverter
Autocompletar ComboBox en JavaFX (8)
Basado en la respuesta de Jonatan , pude construir la siguiente solución:
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;
}
}
Estoy buscando una manera de agregar autocompletar a un JavaFX ComboBox
. Después de buscar mucho, es hora de preguntar aquí.
Este AutoFillBox
es conocido, pero no lo que estoy buscando. Lo que quiero es un combobox editable y mientras tipeo la lista debería ser filtrada. Pero también quiero abrir la lista sin escribir y ver todos los elementos.
¿Alguna idea?
Con la biblioteca ControlsFX puede hacerlo con dos líneas de código:
comboBox.setEditable(true);
TextFields.bindAutoCompletion(comboBox.getEditor(), comboBox.getItems());
Encontré una solución que funciona para mí:
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;
}
}
Puedes llamarlo con
new AutoCompleteComboBoxListener<>(comboBox);
Está basado en esto y lo personalicé para satisfacer mis necesidades.
Siéntete libre de usarlo y si alguien puede mejorarlo, dímelo.
Miro a mi alrededor y pruebo algo. Esto se ve bien:
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 );
}
}
Un Keyhandle para seleccionar el elemento con el carácter de inicio coincidente.
Espero que esto te ayude
No sé si esto todavía es relevante, pero llegó a este tema. Intenté los enfoques superiores, hice una variación de mí mismo. Pero, con mucho, este blog de FXPPS resultó ser el más atractivo (pero tuve que hacer algunas modificaciones ya que mi lista de elementos no era estática, pero aún así ...). Espero que esto tenga la próxima persona que encuentre este tema interesante.
Para agregar al código de Mateus, lo siguiente creará un texto de solicitud para completar automáticamente. Por ejemplo, si escribió "s", un elemento que comience con "s" del ObservableArray (que rellenó el ComboBox) servirá como texto de solicitud. Obviamente, no tendría mucho sentido usarlo con el parámetro "CONTAINING".
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());
}
}
}
Primero, tendrás que crear esta clase en tu proyecto:
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;
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();
} 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 = 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());
}
}
}
Para que tu combobox se complete automáticamente, utilízalo así:
FxUtilTest.autoCompleteComboBoxPlus(myComboBox, (typedText, itemToCompare) -> itemToCompare.getName().toLowerCase().contains(typedText.toLowerCase()) || itemToCompare.getAge().toString().equals(typedText));
También asegúrese de utilizar este método cuando necesite obtener el valor seleccionado del cuadro combinado, de lo contrario, puede enfrentar algunas excepciones como "excepción de lanzamiento de clase":
FxUtilTest.getComboBoxValue(myComboBox);
PD: Hubo algunos problemas con este método en versiones entre jre 8.51 y 8.65 que causaron comportamientos extraños, ahora parece que los problemas ya no suceden. Si enfrenta algún problema, puede ver las ediciones hechas en esta respuesta y obtener la versión anterior que solucionó el problema en ese momento. Este método debe funcionar bien, si tiene algún problema, por favor, hágamelo saber.
aquí hay uno simple
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();
}
});
}
}
y una forma de usarlo:
new AutoShowComboBoxHelper(combo, item -> buildTextToCompare(item));
En aras de la simplicidad, utilicé String :: contiene en este código, para un mejor rendimiento use org.apache.commons.lang3.StringUtils :: containsIgnoreCase