2013-09-25 116 views
8

我想要一個組合框,它按用戶類型篩選列表項。它應該如下:JavaFX - 已篩選組合框

  • 打字的時候,文本字段應顯示一個可能的選擇,但這個詞,用戶尚未輸入的部分應加以強調。
  • 當他打開列表時,下拉菜單應該只顯示可能的選項?
  • 使用箭頭鍵,用戶應該在縮小可能的項目後選擇其中一個剩餘項目。
  • 過濾並不重要,跳到第一個匹配的選擇也可以。

有沒有類似的東西可用?

回答

1

我搜索了類似的一段時間,發現這一點。 請看:

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

} 

您可以

new AutoCompleteComboBoxListener<>(comboBox); 

它基於this調用它,我定製它適合我的需要。

隨意使用它,如果有人能改善它,告訴我。

2

請看:

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

它是基於在此forum找到了答案。希望這可以幫助。

7

就下拉濾波而言。是不是將最佳解決方案包含在FilteredList可能的選項列表中?

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

簡單的解決方案:) – Spl2nky

+0

很好的回答。對於未來的讀者,請參閱我基於Jonatan的實現:https://stackoverflow.com/a/47933342/597657 –