2013-09-25 42 views
8

Tôi muốn một ComboBox, bộ lọc này lọc các mục danh sách như kiểu người dùng. Nó sẽ hoạt động như sau:JavaFX - ComboBox đã lọc

  • Khi nhập, trường văn bản sẽ hiển thị một lựa chọn có thể, nhưng phần của từ mà người dùng chưa nhập phải được tô sáng.
  • Khi mở danh sách, trình đơn thả xuống sẽ chỉ hiển thị các tùy chọn có thể có?
  • Sử dụng các phím mũi tên, người dùng nên chọn một trong các mục còn lại sau khi thu hẹp các mục có thể.
  • Lọc không quan trọng, việc chuyển sang lựa chọn khớp đầu tiên cũng sẽ không sao.

Có điều gì giống như vậy không?

Trả lời

1

Tôi đã tìm kiếm tương tự trong một thời gian và thấy điều này. Hãy xem:

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

} 

Bạn có thể gọi nó với

new AutoCompleteComboBoxListener<>(comboBox); 

Nó dựa trên this và tôi tùy chỉnh nó để phù hợp với nhu cầu của tôi.

Hãy sử dụng nó và nếu có ai có thể cải thiện nó, hãy cho tôi biết.

2

Hãy xem:

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

Nó được dựa trên câu trả lời được tìm thấy trong forum này. Hi vọng điêu nay co ich.

7

Theo như việc lọc thả xuống có liên quan. Không bao gồm danh sách các tùy chọn có thể có trong giải pháp tốt nhất là một giải pháp tốt nhất là 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

giải pháp đơn giản :) – Spl2nky

+0

Tốt câu trả lời. Đối với người đọc trong tương lai, hãy xem triển khai của tôi dựa trên Jonatan's: https://stackoverflow.com/a/47933342/597657 –