2016-01-04 37 views
1

我想創建一個JvafaFX的桌面視圖,它將顯示攝像機參數列表。其中一些參數是可編輯的,有些不是,有些是由值列表限制的,其他參數是自由文本輸入。我已經設置了表格,以便在鍵值顯示中顯示參數,其中一列用作參數的名稱,另一列用於顯示值。如何使用不同類型的可編輯行創建JavaFX Tableview?

爲表中的值是利用從後臺數據模型生成可觀察到的列表中設置:

propertyNamesColumn.setCellValueFactory(cellData -> cellData.getValue().getName()); 
propertyValuesColumn.setCellValueFactory(cellData -> cellData.getValue().getValue()); 

該模型還包含了屬性是否應該爲可編輯,它可以包含這些東西可能值目前存儲在兩個不同的領域(我不知道這是最好的方式),所以總共有4個領域。

當設置表格的行時,我想讓那些應該可編輯的表格(根據模型中的值),可以通過包含可能值列表中的值的選擇框或文本字段進行編輯。

但是我不確定需要實現什麼才能做到這一點,我試圖擴展ChoiceBoxTableCell類以添加此邏輯,但表單元格甚至不可編輯。

我很確定選擇框的單元格類型或細胞工廠的擴展應該能夠做到這一點,但我不知道如何。

感謝您的任何幫助。

+0

您將需要一個電池工廠和一個自定義'TableCell'實現。根據確切的要求(例如,如果和當「可編輯」屬性和任何給定項目的允許值列表可以更改),它可能相當簡單,或者相當複雜。 –

回答

3

我認爲要做到這一點,您需要創建一個通用模型Parameter類,並將其用作表的類型。你可以把它抽象化並定義一個抽象的方法(或者把編輯器工廠委託給另一個類,但我會盡量保持這個儘可能簡單)。然後根據需要定義創建不同編輯器的子類。

這可能是這個樣子:

public abstract class Parameter<T> { 

    private final BooleanProperty editable = new SimpleBooleanProperty(); 

    private final ObjectProperty<T> value = new SimpleObjectProperty<>(); 

    private final String name ; 

    public Parameter(String name, T value, boolean editable) { 
     this.name = name ; 
     setValue(value); 
     setEditable(editable); 
    } 

    public Parameter(String name, T value) { 
     this(name, value, true); 
    } 

    public String getName() { 
     return name ; 
    } 

    public ObjectProperty<T> valueProperty() { 
     return value ; 
    } 

    public T getValue() { 
     return valueProperty().get(); 
    } 

    public void setValue(T value) { 
     valueProperty().set(value); 
    } 

    public BooleanProperty editableProperty() { 
     return editable ; 
    } 

    public boolean isEditable() { 
     return editableProperty().get() ; 
    } 

    public void setEditable(boolean editable) { 
     editableProperty().set(editable); 
    } 

    public abstract Node getEditor() ; 

} 

這時,你可能有這樣一個簡單的實現「免費」的字符串:

public class StringParameter extends Parameter<String> { 

    private final TextField editor ; 

    public StringParameter(String name, String value) { 
     super(name, value); 
     editor = new TextField(); 
     editor.textProperty().bindBidirectional(valueProperty()); 
    } 

    @Override 
    public Node getEditor() { 
     return editor ; 
    } 

} 

,也許像這樣的微調:

public class BoundIntegerParameter extends Parameter<Integer> { 

    private final Spinner<Integer> editor ; 

    public BoundIntegerParameter(int min, int max, String name, int value) { 
     super(name, value); 
     editor = new Spinner<>(min, max, value); 
     editor.setEditable(true); 
     editor.getValueFactory().valueProperty().bindBidirectional(valueProperty()); 
    } 

    @Override 
    public Node getEditor() { 
     return editor ; 
    } 

} 

對於「固定列表」,您可以同樣執行FixedStringParameter取了一個字符串列表,其getEditor方法返回了一個ComboBox。固定選擇另一種方法可能是使用Enum類型:這可能看起來像

public class EnumParameter<E extends Enum<E>> extends Parameter<E> { 

    private final ComboBox<E> editor ; 

    public EnumParameter(String name, E value) { 
     super(name, value); 
     editor = new ComboBox<>(); 
     @SuppressWarnings("unchecked") 
     Class<E> type = (Class<E>) value.getClass(); 
     E[] values = type.getEnumConstants() ; 
     editor.getItems().setAll(values); 
     editor.valueProperty().bindBidirectional(valueProperty()); 
    } 

    @Override 
    public Node getEditor() { 
     return editor ; 
    } 

} 

現在爲值列的單元格執行,你需要有點掛羊頭賣狗肉的。這似乎工作:

public class ParameterValueEditingCell extends TableCell<Parameter<?>, Object> { 


    @Override 
    public void updateItem(Object item, boolean empty) { 
     super.updateItem(item, empty); 
     if (empty || item == null) { 
      setText(null); 
      setGraphic(null); 
     } else { 
      if (isEditing()) { 
       setText(null); 
       Parameter<?> param = getTableView().getItems().get(getIndex()); 
       setGraphic(param.getEditor()); 
      } else { 
       setText(item.toString()); 
       setGraphic(null); 
      } 
     } 
    } 

    @Override 
    public void startEdit() { 
     // check if current parameter is editable and bail if not: 

     int index = getIndex(); 
     if (index < 0 || index >= getTableView().getItems().size()) { 
      return ; 
     } 
     if (! getTableView().getItems().get(index).isEditable()) { 
      return ; 
     } 

     super.startEdit(); 
     setText(null); 
     setGraphic(getTableView().getItems().get(getIndex()).getEditor()); 
    } 

    @Override 
    public void cancelEdit() { 
     super.cancelEdit(); 
     Object item = getItem(); 
     setText(item == null ? null : item.toString()); 
     setGraphic(null); 
    } 

} 

最後,你可以做一些事情,如

TableView<Parameter<?>> table = new TableView<>(); 
table.setEditable(true); 
TableColumn<Parameter<?>, Object> valueColumn = new TableColumn<>("Value"); 

// I can't see any way to set this up without the ugly (unchecked) cast 
// Any ideas? 
valueColumn.setCellValueFactory(cellData -> (ObservableValue<Object>)cellData.getValue().valueProperty()); 

valueColumn.setCellFactory(tc -> new ParameterValueEditingCell()); 

我做了一個完整的例子here

+0

注意這可能不是一個理想的設計,因爲模型('Parameter')擁有對視圖('編輯器)的引用。除了違反基本的UI設計原則之外,對於大量的行來說,這並不能很好地執行(因爲它爲每個項目創建節點,而不是僅爲每個可見行)。改善設計似乎很棘手(至少對我而言);它應該給你至少一個開始。 –

+0

謝謝詹姆斯!我沒有完全使用你的解決方案,因爲我很高興在前端處理所有東西作爲字符串。然而,我確實使用你的單元實現作爲基礎來構建其上的工作很好。我還使用了類似於getEditor()方法的方法來讀取參數類型並返回適當的節點。 我也不得不使用節點的'setOnAction()'方法來調用tableview的'commitEdit(newValue)',否則它們永遠不會被調用。 – fooforever

+0

在我的示例中,由於綁定,因此不需要調用'commitEdit(...)'。我仍然不喜歡模型類中的'getEditor()'方法,但我很高興它有幫助。 –

相關問題