2016-12-13 58 views
0

我已經創建了一個自定義控件調用它ComboBoxTablePopup延伸Comboboxbase類。我已經使用tableview作爲彈出式內容。一切正常, 更新值,顯示彈出,隱藏彈出。在將焦點從ComboBoxTablePopup切換到另一個控件(如TextFieldSpinner)後,它會使用空值更新它自己。 所以,我不知道是什麼讓這種情況發生。所以這裏是我的自執行類的實現。JavaFX Combobox自定義控件焦點丟失問題

import com.sun.javafx.scene.control.behavior.ComboBoxBaseBehavior; 
import com.sun.javafx.scene.control.behavior.KeyBinding; 
import com.sun.javafx.scene.control.skin.ComboBoxListViewSkin; 
import com.sun.javafx.scene.control.skin.ComboBoxPopupControl; 

import javafx.application.Application; 
import javafx.beans.InvalidationListener; 
import javafx.beans.WeakInvalidationListener; 
import javafx.beans.property.ObjectProperty; 
import javafx.beans.property.ReadOnlyObjectProperty; 
import javafx.beans.property.ReadOnlyObjectWrapper; 
import javafx.beans.property.SimpleObjectProperty; 
import javafx.collections.FXCollections; 
import javafx.collections.ListChangeListener; 
import javafx.collections.ObservableList; 
import javafx.collections.WeakListChangeListener; 
import javafx.event.ActionEvent; 
import javafx.scene.Node; 
import javafx.scene.Scene; 
import javafx.scene.control.*; 
import javafx.scene.control.cell.PropertyValueFactory; 
import javafx.scene.input.KeyCode; 
import javafx.scene.layout.VBox; 
import javafx.stage.Stage; 
import javafx.util.StringConverter; 

import java.util.ArrayList; 
import java.util.List; 
import java.util.function.Predicate; 


public class TestComboboxTablePopup extends Application { 
    @Override 
    public void start(Stage primaryStage) throws Exception { 
     ComboBoxTablePopupControl<dataModel> comboBoxTablePopup = new ComboBoxTablePopupControl<>(); 
     TableColumn<dataModel, Integer> tcId = new TableColumn<>("Id"); 
     TableColumn<dataModel, String> tcName = new TableColumn<>("Name"); 
     tcId.setCellValueFactory(new PropertyValueFactory<dataModel, Integer>("id")); 
     tcName.setCellValueFactory(new PropertyValueFactory<dataModel, String>("name")); 

     comboBoxTablePopup.setColumns(FXCollections.observableArrayList(tcId, tcName)); 
     comboBoxTablePopup.setItems(FXCollections.observableArrayList(
       new dataModel(1, "Data Model object 1"), 
       new dataModel(2, "Data Model object 2"), 
       new dataModel(3, "Data Model object 3") 
     )); 

     VBox vBox = new VBox(comboBoxTablePopup); 

     Scene scene = new Scene(vBox); 
     primaryStage.setScene(scene); 
     primaryStage.setWidth(400); 
     primaryStage.setHeight(300); 
     primaryStage.show(); 
    } 


    public class dataModel { 
     private int id; 
     private String name; 

     public dataModel(int id, String name) { 
      this.id = id; 
      this.name = name; 
     } 

     public int getId() { 
      return id; 
     } 

     public void setId(int id) { 
      this.id = id; 
     } 

     public String getName() { 
      return name; 
     } 

     public void setName(String name) { 
      this.name = name; 
     } 
    } 

    private static <S> StringConverter<S> defaultStringConverter() { 
     return new StringConverter<S>() { 
      @Override 
      public String toString(S t) { 
       return t == null ? "abood fait" : t.toString(); 
      } 

      @Override 
      public S fromString(String string) { 
       return null; 
      } 
     }; 
    } 


    private class ComboBoxTablePopupControl<S> extends ComboBoxBase { 


     /*************************************************************************** 
     * * 
     * Static properties and methods           * 
     * * 
     **************************************************************************/ 

     private static final String DEFAULT_STYLE_CLASS = "combobox-table-popup"; 


     private ObjectProperty<ObservableList<S>> items = new SimpleObjectProperty<ObservableList<S>>(this, "items"); 

     public final void setItems(ObservableList<S> value) { 
      itemsProperty().set(value); 
     } 

     public final ObservableList<S> getItems() { 
      return items.get(); 
     } 

     public ObjectProperty<ObservableList<S>> itemsProperty() { 
      return items; 
     } 


     public ObjectProperty<StringConverter<S>> converterProperty() { 
      return converter; 
     } 

     private ObjectProperty<StringConverter<S>> converter = 
       new SimpleObjectProperty<StringConverter<S>>(this, "converter", defaultStringConverter()); 

     public final void setConverter(StringConverter<S> value) { 
      converterProperty().set(value); 
     } 

     public final StringConverter<S> getConverter() { 
      return converterProperty().get(); 
     } 


     // Editor 
     private ReadOnlyObjectWrapper<TextField> editor; 

     public final TextField getEditor() { 
      return editorProperty().get(); 
     } 

     public final ReadOnlyObjectProperty<TextField> editorProperty() { 
      if (editor == null) { 
       editor = new ReadOnlyObjectWrapper<TextField>(this, "editor"); 
       editor.set(new ComboBoxListViewSkin.FakeFocusTextField()); 
      } 
      return editor.getReadOnlyProperty(); 
     } 


     private 
     ObservableList<TableColumn<S, ?>> columns = FXCollections.observableArrayList(); 

     public ObservableList<TableColumn<S, ?>> getColumns() { 
      return columns; 
     } 

     public void setColumns(ObservableList<TableColumn<S, ?>> columns) { 
      this.columns = columns; 
     } 


     /*************************************************************************** 
     *                   * 
     * Constructors               * 
     *                   * 
     **************************************************************************/ 

     /** 
     * Creates a default ComboboxTablePopup instance with an empty 
     * {@link #itemsProperty() items} list and default 
     * {@link #selectionModelProperty() selection model}. 
     */ 


     public ComboBoxTablePopupControl() { 
      this(FXCollections.<S>emptyObservableList()); 

     } 


     /** 
     * Creates a default ComboboxTablePopup instance with the provided items list and 
     * a default { selection model}. 
     */ 

     public ComboBoxTablePopupControl(ObservableList<S> items) { 
      setItems(items); 
      getStyleClass().add(DEFAULT_STYLE_CLASS); 
      setEditable(true); 
      valueProperty().addListener((observable, oldValue, newValue) -> { 
       System.out.println(newValue); 
      }); 


     } 

     public ComboBoxTablePopupControl(ObservableList<S> items, ObservableList<TableColumn<S, ?>> columns) { 
      this(items); 
      this.columns = columns; 

     } 


     @Override 
     protected Skin<?> createDefaultSkin() { 
      return new ComboBoxTablePopupControlSkin<>(this); 
     } 


    } 


    public class ComboBoxTablePopupControlSkin<S> extends ComboBoxPopupControl { 

     private ComboBoxTablePopupControl comboBoxTablePopup; 
     private ObservableList<S> comboboxTablePopupItems; 

     private TableView<S> tableViewPopupContent; 

     private ObservableList<S> tableViewPopupItems; 


     private Predicate<S> predicate; 

     private final InvalidationListener itemsObserver; 

     private final ListChangeListener<S> tableViewItemsListener = new ListChangeListener<S>() { 
      @Override 
      public void onChanged(ListChangeListener.Change<? extends S> c) { 
       getSkinnable().requestLayout(); 
      } 
     }; 

     private final WeakListChangeListener<S> weakListViewItemsListener = 
       new WeakListChangeListener<S>(tableViewItemsListener); 


     public ComboBoxTablePopupControlSkin(ComboBoxTablePopupControl comboBoxTablePopup) { 
      super(comboBoxTablePopup, new ComboBoxBaseBehavior(comboBoxTablePopup, null)); 
      this.comboBoxTablePopup = comboBoxTablePopup; 


      updateComboBoxTablePopupItems(); 


      itemsObserver = observable -> { 
       updateComboBoxTablePopupItems(); 
       updateTableViewItems(); 

      }; 
      this.comboBoxTablePopup.itemsProperty().addListener(new WeakInvalidationListener(itemsObserver)); 

      tableViewPopupContent = createTableView(); 
      tableViewPopupContent.setManaged(false); 

      getChildren().add(tableViewPopupContent); 


      updateTableViewItems(); 


      registerChangeListener(comboBoxTablePopup.converterProperty(), "CONVERTER"); 
      registerChangeListener(comboBoxTablePopup.itemsProperty(), "ITEMS"); 
      registerChangeListener(comboBoxTablePopup.valueProperty(), "VALUE"); 
      registerChangeListener(comboBoxTablePopup.editorProperty(), "EDITABLE"); 


     } 

     private void updateTableViewItems() { 
      this.tableViewPopupItems = comboBoxTablePopup.getItems(); 
      this.tableViewPopupContent.setItems(this.tableViewPopupItems); 

      if (tableViewPopupItems != null) { 
       tableViewPopupItems.removeListener(weakListViewItemsListener); 
      } 

      this.tableViewPopupItems = comboboxTablePopupItems; 
      tableViewPopupContent.setItems(tableViewPopupItems); 

      if (tableViewPopupItems != null) { 
       tableViewPopupItems.addListener(weakListViewItemsListener); 
      } 


      getSkinnable().requestLayout(); 
     } 


     public void updateComboBoxTablePopupItems() { 
      comboboxTablePopupItems = comboBoxTablePopup.getItems(); 
      comboboxTablePopupItems = comboboxTablePopupItems == null ? FXCollections.<S>emptyObservableList() : comboboxTablePopupItems; 

     } 

     private TableView<S> createTableView() { 
      final TableView<S> tableView = new TableView<>(); 

      tableView.setId("table-view"); 
      tableView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); 
      tableView.setFocusTraversable(false); 

      for (TableColumn tblColumn : tableColumns()) { 
       tableView.getColumns().add(tblColumn); 
      } 

      tableView.getSelectionModel().selectedItemProperty().addListener(o -> { 
       S selectedItem = tableView.getSelectionModel().getSelectedItem(); 
       comboBoxTablePopup.setValue(selectedItem); 

      }); 


      tableView.setOnKeyPressed(e -> { 
       if (e.getCode() == KeyCode.ENTER || 

         e.getCode() == KeyCode.SPACE) { 

        S selectedItem = tableView.getSelectionModel().getSelectedItem(); 
        comboBoxTablePopup.setValue(selectedItem); 
        comboBoxTablePopup.hide(); 
       } 

      }); 


      return tableView; 
     } 

     private ObservableList<TableColumn> tableColumns() { 
      return ((ComboBoxTablePopupControl) getSkinnable()).getColumns(); 
     } 

     @Override 
     protected Node getPopupContent() { 
      return this.tableViewPopupContent; 
     } 

     @Override 
     protected TextField getEditor() { 
      return ((ComboBoxTablePopupControl) getSkinnable()).getEditor(); 
     } 

     @Override 
     protected StringConverter<S> getConverter() { 
      return ((ComboBoxTablePopupControl) getSkinnable()).getConverter(); 
     } 

     @Override 
     public Node getDisplayNode() { 

      Node displayNode; 
      displayNode = getEditableInputNode(); 


      updateDisplayNode(); 

      return displayNode; 

     } 

     @Override 
     protected void handleControlPropertyChanged(String p) { 


      if ("VALUE".equals(p)) { 

       updateDisplayNode(); 
       System.out.println(comboBoxTablePopup.getValue()); 
       comboBoxTablePopup.fireEvent(new ActionEvent()); 
      } else if ("CONVERTER".equals(p)) { 
       updateDisplayNode(); 
       System.out.println("Conveter peroptery"); 
      } else if ("ITEMS".equals(p)) { 

       updateComboBoxTablePopupItems(); 
       updateTableViewItems(); 
      } else if ("EDITOR".equals(p)) { 
       getEditableInputNode(); 
      } else 
       super.handleControlPropertyChanged(p); 
     } 


    } 


} 
+1

哇,這是的代碼加載。它必須是您的控件的價值設置在第一位(或數據綁定到您的控件)的某個點。也許你可以指出這是怎麼發生的,或者至少在示例應用程序中添加一個關於如何使用控件的示例 - 這可能有助於在沒有太多調查的情況下開始使用。 – Fuzzzzel

+0

我已經添加了一個完整的運行示例,請嘗試找出我的問題。 –

回答

0

如果有人仍然有這個問題的麻煩,這裏是一個選擇框的解決方法。我假設你已經有一張表格和一個EventHandler。此示例未使用任何可編輯isEditable(true))對象進行測試。

首先設置自定義FactorysetOnEditStart

yourColumn.setCellFactory(value -> new CustomEditFactory(yourObsList)); 

的事件處理程序在你CustomEditFactory類必須擴展TreeTableCell<KnotenObs, String>@Override四種方法

startEdit中()

@Override 
    public void startEdit() { 
     super.startEdit();   
     choiceBox.getSelectionModel().select(getItem()); 
     oldValue = choiceBox.getSelectionModel().getSelectedItem(); 
     setGraphic(choiceBox); 
     setContentDisplay(ContentDisplay.GRAPHIC_ONLY); 
    } 

commitEdit的()

@Override 
    public void commitEdit(String newValue) { 
     if(newValue == null) { 
      super.commitEdit(oldValue); 
     } else { 
      super.commitEdit(newValue); 
     } 
     setContentDisplay(ContentDisplay.TEXT_ONLY); 
    } 

的CancelEdit()

@Override 
    public void cancelEdit() { 
     super.cancelEdit(); 
     if(getItem() == null) { 
      setText(oldValue); 
     } else { 
      setText(getItem()); 
     } 
     setContentDisplay(ContentDisplay.TEXT_ONLY); 
    } 

的updateItem()

@Override 
    public void updateItem(String item, boolean empty) { 
     if(item == null) { 
      super.updateItem(oldValue, empty); 
      setText(oldValue); 
     } else { 
      super.updateItem(item, empty); 
      setText(item); 
     } 
     setContentDisplay(ContentDisplay.TEXT_ONLY); 
    }