2016-06-27 6 views
1

一つは、どのようなユーザー・タイプに基づいてオートフィルタするコンボボックスを拡張しようとしていますJavaFX ComboBoxのバグ?私の開発者の

public class AutoCompleteComboBox<T> extends ComboBox<T> { 

    private FilteredList<T> filteredItems; 
    private SortedList<T> sortedItems; 

    public AutoCompleteComboBox() { 
     setEditable(true); 
     setOnKeyReleased(e -> handleOnKeyReleasedEvent(e)); 
     setOnMouseClicked(e -> handleOnMouseClicked(e)); 
    } 

    private void handleOnMouseClicked(MouseEvent e) { 
     getItems().stream() 
       .filter(item -> item.toString().equals(getEditor().getText())) 
       .forEach(item -> getSelectionModel().select(item)); 
     setCaretPositionToEnd(); 
    } 

    private void handleOnKeyReleasedEvent(KeyEvent e) { 
     if (e.getCode() == KeyCode.UP || e.getCode() == KeyCode.DOWN) { 
      getItems().stream() 
       .filter(item -> item.toString().equals(getEditor().getText())) 
       .forEach(item -> getSelectionModel().select(item)); 
      show(); 
      setCaretPositionToEnd(); 
     } else if (e.getCode() == KeyCode.ENTER || e.getCode() == KeyCode.TAB) { 
       String editorStr = getEditor().getText(); 
       getSelectionModel().clearSelection(); 
       getEditor().setText(editorStr); 
       setItems(this.sortedItems); 
       getItems().stream() 
        .filter(item -> item.toString().equals(editorStr)) 
        .forEach(item -> getSelectionModel().select(item)); 

       getEditor().selectEnd(); 
       if (e.getCode() == KeyCode.ENTER) { 
        getEditor().deselect(); 
       } 
       hide(); 
     } else if (e.getText().length() == 1) { 
      getSelectionModel().clearSelection(); 
      if (getEditor().getText().length() == 0) { 
       getEditor().setText(e.getText()); 
      } 
      filterSelectionList(); 
      show(); 
     } else if (e.getCode() == KeyCode.BACK_SPACE && getEditor().getText().length() > 0) { 
      String editorStr = getEditor().getText(); 
      getSelectionModel().clearSelection(); 
      getEditor().setText(editorStr); 
      int beforeFilter = getItems().size(); 
      filterSelectionList(); 
      int afterFilter = getItems().size(); 
      if (afterFilter > beforeFilter) { 
       hide(); 
      } 
      show(); 
     } else if (e.getCode() == KeyCode.BACK_SPACE && getEditor().getText().length() == 0) { 
      clearSelection(); 
      hide(); 
      show(); 
     } 
    } 

    private void filterSelectionList() { 
     setFilteredItems(); 
     setCaretPositionToEnd(); 
    } 

    private void setFilteredItems() { 
     filteredItems.setPredicate(item -> 
       item.toString().toLowerCase().startsWith(getEditor().getText().toLowerCase())); 
    } 

    private void setCaretPositionToEnd() { 
     getEditor().selectEnd(); 
     getEditor().deselect(); 
    } 

    public void setInitItems(ObservableList<T> values) { 
     filteredItems = new FilteredList<>(values); 
     sortedItems = new SortedList<>(filteredItems); 
     setItems(this.sortedItems); 
    } 

    public void clearSelection() { 
     getSelectionModel().clearSelection(); 
     getEditor().clear(); 
     if (this.filteredItems != null) { 
      this.filteredItems.setPredicate(item -> true); 
     } 
    } 

    public T getSelectedItem() { 
     T selectedItem = null; 
     if (getSelectionModel().getSelectedIndex() > -1) { 
      selectedItem = getItems().get(getSelectionModel().getSelectedIndex()); 
     } 
     return selectedItem; 
    } 

    public void select(String value) { 
     if (!value.isEmpty()) { 
      getItems().stream() 
       .filter(item -> value.equals(item.toString())) 
       .findFirst() 
       .ifPresent(item -> getSelectionModel().select(item)); 
     } 
    } 
} 

制御がうまく動作しますが、私はどちらかの選択またはValueプロパティに何かをバインドまで。そのようなことが起こると、フィールドを離れると(またはアクションが発生するように入力すると)、次の例外が発生します。 (単にマウスで値を選択するだけで、問題なく選択されます)このコントロールでバインドするのは、CodeTableValueの単純なオブジェクトです.2つのプロパティ(両方のStrings、コード& Value)があります。 toStringはValueプロパティを返します。

コントロールがリスナーなしで設定されている場合は、正常に動作します。しかし、値のプロパティの1つを聞くとすぐに失敗します。

私は私のクラスでAutoCompleteComboBoxコントロールを使用する場合:

@FXML private AutoCompleteComboBox<CodeTableValue> myAutoCompleteBox; 

myAutoCompleteBox.getSelectionModel().selectedItemProperty().addListener((obs, ov, nv) -> { 
       System.out.println(nv); 
}); 

スローされる例外:

Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: java.lang.String cannot be cast to cache.CodeTableValue 
     at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:361) 
     at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81) 
     at javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:74) 
     at javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:102) 
     at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112) 
     at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:146) 
     at javafx.scene.control.SelectionModel.setSelectedItem(SelectionModel.java:102) 
     at javafx.scene.control.ComboBox.lambda$new$152(ComboBox.java:249) 
     at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:361) 
     at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81) 
     at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(ObjectPropertyBase.java:105) 
     at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112) 
     at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:146) 
     at javafx.scene.control.ComboBoxBase.setValue(ComboBoxBase.java:150) 
     at com.sun.javafx.scene.control.skin.ComboBoxPopupControl.setTextFromTextFieldIntoComboBoxValue(ComboBoxPopupControl.java:405) 
     at com.sun.javafx.scene.control.skin.ComboBoxPopupControl.lambda$new$291(ComboBoxPopupControl.java:82) 
     at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:361) 
     at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81) 
     at javafx.beans.property.ReadOnlyBooleanPropertyBase.fireValueChangedEvent(ReadOnlyBooleanPropertyBase.java:72) 
     at javafx.scene.Node$FocusedProperty.notifyListeners(Node.java:7718) 
     at javafx.scene.Node.setFocused(Node.java:7771) 
     at javafx.scene.Scene$KeyHandler.setWindowFocused(Scene.java:3932) 
     at javafx.scene.Scene$KeyHandler.lambda$new$11(Scene.java:3954) 
     at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:137) 
     at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81) 
     at javafx.beans.property.ReadOnlyBooleanPropertyBase.fireValueChangedEvent(ReadOnlyBooleanPropertyBase.java:72) 
     at javafx.beans.property.ReadOnlyBooleanWrapper.fireValueChangedEvent(ReadOnlyBooleanWrapper.java:103) 
     at javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:110) 
     at javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:144) 
     at javafx.stage.Window.setFocused(Window.java:439) 
     at com.sun.javafx.stage.WindowPeerListener.changedFocused(WindowPeerListener.java:59) 
     at com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:100) 
     at com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:40) 
     at java.security.AccessController.doPrivileged(Native Method) 
     at com.sun.javafx.tk.quantum.GlassWindowEventHandler.lambda$handleWindowEvent$423(GlassWindowEventHandler.java:150) 
     at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389) 
     at com.sun.javafx.tk.quantum.GlassWindowEventHandler.handleWindowEvent(GlassWindowEventHandler.java:148) 
     at com.sun.glass.ui.Window.handleWindowEvent(Window.java:1266) 
     at com.sun.glass.ui.Window.notifyFocus(Window.java:1245) 
     at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) 
     at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191) 
     at java.lang.Thread.run(Thread.java:745) 

をeditableFlagがfalseの場合、これは動作します。ユーザーがコンテンツをフィルタリングするためにコンボボックスに入力することを許可した場合にのみ発生します。バインディングがなければ、例外はなく、値は正しく設定されます。

私はこの例外を深く掘ってきた、それはここで投げているが:

com.sun.javafx.binding.ExpressionHelper.Generic.fireValueChangedEvent() 

       if (curChangeSize > 0) { 
        final T oldValue = currentValue; 
        currentValue = observable.getValue(); 
        final boolean changed = (currentValue == null)? (oldValue != null) : !currentValue.equals(oldValue); 
        if (changed) { 
         for (int i = 0; i < curChangeSize; i++) { 
          try { 
           curChangeList[i].changed(observable, oldValue, currentValue); 
          } catch (Exception e) { 
           Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e); 
          } 
         } 
        } 
       } 

それはvaluePropertyまたはselectedItemPropertyがバインドされている場合のみ、ここから来ています。それ以外の場合は、この問題は発生しません。

javafx.beans.property.ObjectPropertyBase.set(T) @Override 
    public void set(T newValue) { 
     if (isBound()) { 
      throw new java.lang.RuntimeException((getBean() != null && getName() != null ? 
        getBean().getClass().getSimpleName() + "." + getName() + " : ": "") + "A bound value cannot be set."); 
     } 
     if (value != newValue) { 
      value = newValue; 
      markInvalid(); 
     } 
    } 

私はそれが問題を修正でしょうかどうかを確認するために、エディタにStringConverterを追加しようとしたが、再び、それはこの例外をスローし続けています。おそらく、私たちはこれをすべて間違って処理しようとしていますか?

基本的に、ユーザーがフィールドに入力するときにコンボボックスの選択項目をフィルタリングすることを検討しています。これを処理する方法が異なる場合はお知らせくださいが、現時点ではJDKのバグである可能性はありますか?フィールドがバインドされていない場合は問題はありませんが、一度バインドされると、フィールドのフォーカスが失われたか、Enterキーが押されたときにこの例外が発生します。

答えて

1

私はあなたのコントロールを使用して迅速なサンプルを作成しました:私はかつて私は終えるタイピングあなたの例外を再現することができますし、コミットするために入力する]をクリックします

public class CodeTableValue { 

    private String code; 
    private String value; 

    public CodeTableValue(String code, String value) { 
     this.code = code; 
     this.value = value; 
    } 

    public String getCode() { 
     return code; 
    } 

    public void setCode(String code) { 
     this.code = code; 
    } 

    public String getValue() { 
     return value; 
    } 

    public void setValue(String value) { 
     this.value = value; 
    } 

    @Override 
    public String toString() { 
     return "code=" + code + ", value=" + value; 
    } 
} 

:単純なモデルクラスに基づいて

@Override 
public void start(Stage primaryStage) { 

    AutoCompleteComboBox<CodeTableValue> myAutoCompleteBox = 
      new AutoCompleteComboBox<>(); 
    myAutoCompleteBox.setInitItems(FXCollections.observableArrayList(new CodeTableValue("One", "1"), 
      new CodeTableValue("Two", "2"), new CodeTableValue("Three", "4"))); 

    StackPane root = new StackPane(myAutoCompleteBox); 
    myAutoCompleteBox.getSelectionModel().selectedItemProperty().addListener((obs, ov, nv) -> { 
        System.out.println(nv); 
     }); 

    Scene scene = new Scene(root, 300, 250); 
    primaryStage.setScene(scene); 
    primaryStage.show(); 
} 

値を入力してコントロールを終了します。

java.lang.ClassCastException: java.lang.String cannot be cast to CodeTableValue 

例外は、 TextFieldをComboBoxから編集モードで使用すると、常にStringが返されますが、カスタムComboBoxにはCodeTableValueクラスを使用させます。

解決策は、文字列とCodeTableValueの両方をStringConverterを使用して変換する方法を提供しています。 ClassCastExceptionせず、これが機能するようになりました

AutoCompleteComboBox<CodeTableValue> myAutoCompleteBox = 
      new AutoCompleteComboBox<>(new StringConverter<CodeTableValue>() { 

     @Override 
     public String toString(CodeTableValue object) { 
      if (object != null) { 
       return object.getValue(); 
      } 
      return null; 
     } 

     @Override 
     public CodeTableValue fromString(String string) { 
      return new CodeTableValue(string, string); 
     } 

    }); 

:サンプル中

public AutoCompleteComboBox(StringConverter<T> converter) { 
    setEditable(true); 
    setOnKeyReleased(e -> handleOnKeyReleasedEvent(e)); 
    setOnMouseClicked(e -> handleOnMouseClicked(e)); 

    super.setConverter(converter); 
} 

となりました:

は、だから私はあなたのコントロールを変更しました。

明らかに、文字列を入力する方法(codeまたはvalueのいずれか)を入力し、もう一方を検索する方法を提供する必要があります。

+0

ありがとうございます!これは機能します。私はエディタではなく、コントロールをコンバータを入れていた! –

関連する問題