2016-02-24 3 views
2

У меня есть двигатель, который содержит детали в списке, как:JavaFX TableView, содержащий ListView обновления, CellValueFactory

enter image description here

UPDATE: я обновил текст привести пример

Я хотел бы иметь TableView двигателей, который содержит 2 столбца (имя, части). Я хочу, чтобы части Column отображались как ListView в TableCell, поэтому я переопределяю CellFactory этого столбца.

import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; 

public class Part { 
    private final SimpleStringProperty name = new SimpleStringProperty(""); 
    // Reference to the parent engine 
    private final ObjectProperty<Engine> engine= new SimpleObjectProperty<>(null); 

    public Part(String name, Engine engine) { 
     this.setName(name); 
     this.setEngine(engine); 
    } 

    public ObjectProperty<Engine> engineProperty() { 
     return engine; 
    } 

    public Engine getEngine() { 
     return engine.get(); 
    } 

    public void setEngine(Engine engine) { 
     this.engine.set(engine); 
    } 

    public SimpleStringProperty nameProperty() { 
     return name; 
    } 

    public String getName() { 
     return name.get(); 
    } 

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

Класс двигателя:

import javafx.beans.Observable; 
import javafx.beans.property.SimpleStringProperty; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.util.Callback; 

public class Engine { 
    private final SimpleStringProperty name = new SimpleStringProperty(""); 

    Callback<Part, Observable[]> partsExtractor = part -> new Observable[] { 
      part.nameProperty(), 
      part.engineProperty() 
    }; 

    private final ObservableList<Part> parts = FXCollections.observableArrayList(partsExtractor); 

    public Engine(String name) { 
     this.setName(name); 
    } 

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

    public String getName() { 
     return name.get(); 
    } 

    public SimpleStringProperty nameProperty() { 
     return name; 
    } 

    public ObservableList<Part> getParts() { 
     return parts; 
    } 
} 

Применение:

import javafx.application.Application; 
import javafx.beans.Observable; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.scene.Scene; 
import javafx.scene.control.*; 
import javafx.scene.control.cell.PropertyValueFactory; 
import javafx.scene.control.cell.TextFieldTableCell; 
import javafx.scene.layout.StackPane; 
import javafx.stage.Stage; 
import javafx.util.Callback; 

/** 
* If you edit the field name of the engine on the table the changed name does not reflect on the listView within the 
* table cell, it should actually show the new name of the engine 
*/ 

class PartListCell extends ListCell<Part> { 
    private Label name = new Label(); 

    public PartListCell() { 

    } 

    @Override 
    protected void updateItem(Part part, boolean empty) { 
     super.updateItem(part, empty); 

     if(!empty && part != null) { 
      name.setText(part.getEngine().getName() + "/" + part.getName()); 
      setGraphic(name); 
     } else { 
      setGraphic(null); 
     } 
    } 
} 

class PartsTableCell extends TableCell<Engine, ObservableList<Part>> { 
    private final ListView<Part> listView = new ListView<>(); 

    public PartsTableCell() { 
     listView.setCellFactory(listView -> new PartListCell()); 
    } 

    @Override 
    protected void updateItem(ObservableList<Part> parts, boolean empty) { 
     if(!empty && parts != null){ 
      listView.setItems(parts); 
      setGraphic(listView); 
     } else { 
      setGraphic(null); 
     } 
    } 
} 

public class Main extends Application { 

    Callback<Engine, Observable[]> engineExtractor = engine -> new Observable[] { 
      engine.nameProperty(), 
    }; 

    private final ObservableList<Engine> engines = FXCollections.observableArrayList(engineExtractor); 

    public Main() { 
     Engine engine = new Engine("Engine-1"); 
     Part part1 = new Part("Part-1", engine); 
     Part part2 = new Part("Part-1", engine); 

     engine.getParts().addAll(part1, part2); 

     engines.add(engine); 
    } 

    @Override 
    public void start(Stage primaryStage) throws Exception { 
     StackPane root = new StackPane(); 

     TableColumn<Engine, String> nameColumn = new TableColumn<>("Name"); 
     TableColumn<Engine, ObservableList<Part>> partsColumn = new TableColumn<>("Parts"); 

     // Set the Cell Factories 
     nameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty()); 
     // Make an editable text field 
     nameColumn.setCellFactory(TextFieldTableCell.forTableColumn()); 
     nameColumn.setOnEditCommit(event -> event.getTableView().getItems().get(
       event.getTablePosition().getRow()).setName(event.getNewValue())); 

     partsColumn.setCellFactory(param -> new PartsTableCell()); 
     partsColumn.setCellValueFactory(new PropertyValueFactory<>("parts")); 

     TableView<Engine> tableView = new TableView<>(engines); 
     tableView.setEditable(true); 
     tableView.getColumns().add(nameColumn); 
     tableView.getColumns().add(partsColumn); 

     // Set the stage 
     root.getChildren().add(tableView); 
     primaryStage.setScene(new Scene(root, 400, 400)); 
     primaryStage.show(); 
    } 
} 

Но как я создать CellValueFactory, потому

partsColumn.setCellValueFactory(new PropertyValueFactory<>("parts")); 

обыкновение делать это, так как ей обернется и если я изменяю часть в списке деталей, она не обновляет детали в списке деталей. Мне нужно создать обратный вызов, который предоставляет список частей в качестве наблюдаемого для Table Cell, но я не знаю, как это сделать. Кто-нибудь за помощь?

+0

hmm ... возможно, не совсем понимаю вашу настройку: каков тип элементов таблицы? Похоже на что-то LineItem (Engine, Parts)? Если это так, вам понадобится экстрактор в списке _table.items_, который прослушивает изменения частей. Если нет, процедура ol: просьба предоставить SSCCE, который демонстрирует проблему :-) – kleopatra

+0

@ kleopatra i предоставил пример выше, к сожалению, мне пришлось создать 2 дополнительных файла, потому что у меня возникла ошибка отражения, когда я создал фабрику свойств Value – pfried

+0

unrelated: вам не нужен commitHandler - автоматы делают это, если свойство доступно для записи – kleopatra

ответ

2

не ответ, слишком долго для комментария:

Еще не совсем уверен, если я понимаю, ваше требование правильно: но вот экстрактор, который делает то, что вы хотите:

Callback<Part, Observable[]> partsExtractor = part -> new Observable[] { 
      part.nameProperty(), part.engineProperty().get().nameProperty() }; 
+0

да, thats в основном это, я не знал, что я могу получить родительские свойства с '.get()', он работает, как я хочу. – pfried

2

Ваш PartListCell просто устанавливает текст его метки в текущее значение имени двигателя, объединенного с именем детали. Если часть, отображаемая ячейкой, изменится, она будет обновляться (updateItem(...) будет вызываться снова), но ни ячейка, ни метка не наблюдают за именем двигателя. Следовательно, если имя движка изменяется, ячейка не знает, что его необходимо обновить.

Исправление просто привязать текст метки к соответствующему StringExpression:

class PartListCell extends ListCell<Part> { 
    private Label name = new Label(); 

    public PartListCell() { 

    } 

    @Override 
    protected void updateItem(Part part, boolean empty) { 
     super.updateItem(part, empty); 

     name.textProperty().unbind(); 

     if(!empty && part != null) { 
      name.textProperty().bind(part.getEngine().nameProperty().concat("/").concat(part.nameProperty())); 
      setGraphic(name); 
     } else { 
      name.setText(null); 
      setGraphic(null); 
     } 
    } 
} 
+0

Спасибо за ваш ответ, я привел пример выше, чтобы показать, в чем моя проблема, я думаю, что я не уточнил до – pfried

+0

ОК, я Получите это сейчас: я не понимал, что ячейка списка нужна для отображения имени двигателя. См. Обновление. –

+0

Спасибо Джеймсу за ответ, я думаю, что решение kleopatra - это то, что я хочу, даже если ваше решение в основном делает его более эффективным. Но если я могу избежать привязки на таком уровне, я бы пошел на экстрактор – pfried

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