2016-01-04 2 views
1

Я пытаюсь создать табличное представление с JvafaFX, которое отобразит список параметров камеры. Некоторые из этих параметров доступны для редактирования, а некоторые нет, некоторые из них ограничены списком значений, другие - свободным вводом текста. Я установил таблицу, чтобы отображать параметры в ключе, тип типа отображения, где один столбец используется для имени параметра, а другое значение.Как создать табличное представление JavaFX с редактируемыми рядами разных типов?

Значение таблицы задается с помощью наблюдаемого списка, который генерируется из модели фона данных:

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

Модели также содержит, будет ли свойство быть редактируемым и какими возможными значениями он может содержать, эти в настоящее время хранятся в двух разных полях (я не уверен, что это лучший способ), поэтому всего 4 поля.

При настройке строк таблицы я хочу сделать те, которые должны быть доступны для редактирования (в соответствии со значениями в модели), редактируемые с помощью выбора, содержащего значения из списка возможных значений или текстового поля.

Однако я не уверен, что мне нужно реализовать, чтобы это произошло. Я попытался расширить класс ChoiceBoxTableCell, чтобы добавить эту логику, но ячейки таблицы никогда не становятся редактируемыми.

Я уверен, что либо расширение типа ячейки выбора или фабрика ячеек должно быть в состоянии сделать это, но я не знаю, как это сделать.

Спасибо за любую помощь.

+0

Вам нужен мобильный завод и обычай 'реализации TableCell'. В зависимости от точных требований (например, если и когда свойство «редактируемого» и список допустимых значений для любого данного элемента могут измениться), это может быть довольно просто или достаточно сложно. –

ответ

3

Я думаю, для этого вам необходимо создать общую модель Parameter и использовать ее как тип для вашей таблицы. Вы можете сделать его абстрактным и определить абстрактный метод getEditor() (или, возможно, делегировать фабрику редактора другому классу, но я постараюсь сделать это максимально простым). Затем определите подклассы, которые при необходимости создают разные редакторы.

Это может выглядеть примерно так:

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') содержит ссылку на представление (' editor'). Помимо нарушения основных принципов дизайна пользовательского интерфейса, это не будет хорошо работать для большого количества строк (потому что он создает узел для каждого элемента, а не только для каждой видимой строки). Улучшение дизайна кажется сложным (для меня, по крайней мере); он должен дать вам хотя бы начало. –

+0

Спасибо, Джеймс! Я не полностью использовал ваше решение, так как я доволен тем, что разбираюсь со всем как строка на передней панели. Тем не менее, я использовал реализацию вашей ячейки в качестве основы для построения, которая отлично работала. Я также использовал аналогичный метод для метода 'getEditor()' для чтения типа параметра и возврата соответствующего узла. Мне также пришлось использовать методы setOnAction() 'для узлов, чтобы вызывать' commitEdit (newValue) 'tableview becuase, иначе они никогда не вызывались. – fooforever

+0

В моем примере получение 'commitEdit (...)' invoked не требуется из-за привязки. Мне все еще не нравится метод getEditor() 'в классе модели, но я рад, что это помогло. –

Смежные вопросы