2013-09-25 10 views
8

Desidero un ComboBox, che filtra gli elementi dell'elenco come i tipi di utente. Dovrebbe funzionare come segue:JavaFX - ComboBox filtrato

  • Durante la digitazione, il campo di testo dovrebbe mostrare una possibile scelta, ma la parte della parola che l'utente non ha ancora digitato dovrebbe essere evidenziata.
  • Quando apre la lista, il menu a tendina dovrebbe mostrare solo le opzioni possibili?
  • Utilizzando i tasti freccia, l'utente deve selezionare uno degli elementi rimanenti dopo aver ristretto gli elementi possibili.
  • Il filtraggio non è così importante, anche il salto alla prima selezione corrispondente andrebbe bene.

C'è qualcosa del genere disponibile?

risposta

1

Ho cercato simile per un po 'e ho trovato questo. Date un'occhiata:

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

} 

Si può chiamare con

new AutoCompleteComboBoxListener<>(comboBox); 

Si basa su this e personalizzato che si adatti alle mie esigenze.

Sentitevi liberi di usarlo e se qualcuno può migliorarlo, dimmelo.

2

Date un'occhiata:

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

Essa si basa sulla risposta che si trova in questo forum. Spero che questo ti aiuti.

7

Per quanto riguarda il filtraggio del menu a discesa. Non è l'involucro dell'elenco di opzioni possibili in un FilteredList la soluzione migliore?

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(); 
    } 
} 
+0

Soluzione semplice :) – Spl2nky

+0

Buona risposta. Per i futuri lettori, vedere la mia implementazione basata su Jonatan: https://stackoverflow.com/a/47933342/597657 –