2017-11-22 7 views
2

Ich habe javafx Tabelle, die Mitglied enthält monatliche Gebühren einer Organisation enthält. In dieser Tabelle zeige ich einige Gebühren mit ihrem Standardbetrag, so dass Benutzer in jedem Fall den Betrag aktualisieren oder so fortfahren können. Was ich tatsächlich brauche, ist, wenn Benutzer irgendwelche Werte aktualisieren, sollten diese sofort an die Tabelle Observable List gebunden werden.JavaFX TableView bind Änderungen in CellValueFactory zu Tabelle Observablelist

erstellen Tabelle:

private void initCollectionTable(ObservableList<Member> mList) { 

    ... 

    total_pay_col.setCellValueFactory(new SubscriptionValueFactory()); 
    detail_view_col.setCellValueFactory(new DisplaySubscriptionFactory()); 

    ... 

    collection_tbl.setItems(mlz); 
} 

SubscriptionValueFactory Klasse:

public class SubscriptionValueFactory implements Callback<TableColumn.CellDataFeatures<Member, String>, ObservableValue<String>> { 

    @Override 
    public ObservableValue<String> call(TableColumn.CellDataFeatures<Member, String> param) { 

     //Here Subscriptions are equal to various member charges 
     Member ml = param.getValue(); 
     List<MemberSubscriptions> mbrSubs = new ArrayList<>(ml.getMemberSubscriptions()); 
     double sum =mbrSubs.stream().mapToDouble(a -> a.getAmount()).sum(); 
     return new SimpleObjectProperty<>(TextFormatHandler.CURRENCY_DECIMAL_FORMAT.format(sum)); 
    } 

} 

DisplaySubscriptionFactory Klasse

public class DisplaySubscriptionFactory implements Callback<TableColumn.CellDataFeatures<Member, Button>, ObservableValue<Button>> { 

    @Override 
    public ObservableValue<Button> call(TableColumn.CellDataFeatures<Member, Button> param) { 
     Member ml = param.getValue(); 

     List<MemberSubscriptions> mbrSubs = new ArrayList<>(ml.getMemberSubscriptions()); 

     double sum =mbrSubs.stream().mapToDouble(a -> a.getAmount()).sum(); 
     boolean flag = // SOME BOOLEAN CHECK HERE----- 

     param.getValue().setTotalSubscription(sum); 
     Button button = new Button("View Info"); 
     button.setOnAction((evt) -> { 
      Alert alert_details = new Alert(Alert.AlertType.INFORMATION); 
      alert_details.setTitle("Subscription Information"); 
      alert_details.setHeaderText("Member Subscription information for each installment"); 
      alert_details.getDialogPane().setContent(createContentGrid(mbrSubs, sum, flag)); 
      alert_details.show(); 
     }); 

     return new SimpleObjectProperty<>(button); 
    } 

Diese Methode erstellt ein Raster, um die Mitgliedsgebühren in Textfeldern anzuzeigen. Jede Wertänderung innerhalb des Textfelds sollte daher seine eigene Eigenschaft der Tabelle Observablenliste aktualisieren.

private Node createContentGrid(List<MemberSubscriptions> mbrSubs, double sum, boolean flag) { 

    GridPane grid = new GridPane(); 
    grid.setHgap(20); 
    grid.setVgap(10); 
    grid.setPadding(new Insets(20, 20, 10, 10)); 

    Label totLabel = new Label(TextFormatHandler.CURRENCY_DECIMAL_FORMAT.format(sum)); 
    totLabel.setFont(Font.font("System Bold", 21.0)); 
    Label col_h_1 = new Label("Subscription"); 
    col_h_1.setFont(Font.font("System Bold", 17.0)); 
    Label col_h_2 = new Label("Amount"); 
    col_h_2.setFont(Font.font("System Bold", 17.0)); 

    grid.add(col_h_1, 0, 0); 
    grid.add(col_h_2, 1, 0); 
    Label[] labels = new Label[mbrSubs.size()]; 
    TextField[] fields = new TextField[mbrSubs.size()]; 
    for (int i = 0; i < mbrSubs.size(); i++) { 
     MemberSubscriptions get = mbrSubs.get(i); 
     labels[i] = new Label("label"); 
     fields[i] = new TextField(); 
     fields[i].setTextFormatter(TextFormatHandler.currencyFormatter()); 
     labels[i].setText(get.getMemberSubscription().getFeeName()); 
     fields[i].setText(TextFormatHandler.CURRENCY_DECIMAL_FORMAT.format(get.getAmount())); 
     grid.add(labels[i], 0, i + 1); 
     grid.add(fields[i], 1, i + 1); 
    } 
    grid.add(totLabel, 1, mbrSubs.size() + 1); 
    return grid; 
} 

Klasse:

public class Member { 
    private Integer id; 
    private String memberId; 
    ... 
    private Set<MemberSubscriptions> memberSubscriptions = new HashSet<>(); 
    ... 

MemberSubscriptions Klasse

public class MemberSubscriptions { 
    private Integer id; 
    private Member member; 
    private Double amount; 
    ... 

enter image description here

+0

Wenn möglich, sollten die 'membersSubscriptions' ein' ObservableSet' und die Eigenschaften von 'MemberSubscription' JavaFX-ähnliche Observables/Properties sein. Ich verstehe auch nicht ganz, warum Sie eine 'MembersSubscriptions'-Klasse mit einem 'MemberSubscription'-Feld brauchen - sollte nicht die Menge in' Member' aus tatsächlichen Abonnements bestehen, anstatt ... was 'MemberSubscriptions' ist? – Itai

+1

Zuerst übergibt 'initCollectionTable() 'in' mList', aber 'setItems()' verwendet 'mlz', das aus dem Nichts kommt.Zweitens sollten Sie keine Schaltfläche in 'setCellValueFactory()' erstellen, sondern in 'setCellFactory()'. Drittens müssen Sie in Ihren Klassen "Member" und "MemberSubscriptions" Eigenschaften wie "IntegerProperty" verwenden. Viertens sollten Sie 'ObservableList' verwenden, das mit' FXCollections.observableArrayList (Callback Extractor) instanziiert wird '. – Jai

+0

@sillyfly Entschuldigung wegen des Fehlers ... Ich habe die MemberSubscriptions-Klasse aktualisiert. –

Antwort

1

Ich glaube, ich habe die Antwort auf Ihre Frage gefunden. Ich habe es vereinfacht, also können Sie es vielleicht verwenden, um Ihr Problem zu lösen. Hier ist mein Code für meine Klassen:

Modell:

public class Model { 

    private LongProperty id; 
    private DoubleProperty amount; 
    private ObjectProperty<SubModel> subModel; 

    public Model(Long id, Double amount, SubModel subModel) { 
     this.id = new SimpleLongProperty(id); 
     this.amount = new SimpleDoubleProperty(amount); 
     this.subModel = new SimpleObjectProperty<>(subModel); 
    } 

    public long getId() { 
     return id.get(); 
    } 

    public LongProperty idProperty() { 
     return id; 
    } 

    public DoubleProperty amountProperty() { 
     return amount; 
    } 

    public SubModel getSubModel() { 
     return subModel.get(); 
    } 

    public ObjectProperty<SubModel> subModelProperty() { 
     return subModel; 
    } 
} 

Submodells:

public class SubModel { 

    private LongProperty id; 
    private DoubleProperty xAmount; 
    private DoubleProperty yAmount; 

    public SubModel(Long id, Double xAmount, Double yAmount) { 
     this.id = new SimpleLongProperty(id); 
     this.xAmount = new SimpleDoubleProperty(xAmount); 
     this.yAmount = new SimpleDoubleProperty(yAmount); 
    } 

    public long getId() { 
     return id.get(); 
    } 


    public double getxAmount() { 
     return xAmount.get(); 
    } 

    public DoubleProperty xAmountProperty() { 
     return xAmount; 
    } 

    public double getyAmount() { 
     return yAmount.get(); 
    } 

    public DoubleProperty yAmountProperty() { 
     return yAmount; 
    } 
} 

Knopfzelle:

public class ButtonTableCell<S, T> extends TableCell<S, T> { 

    private Button button; 

    public ButtonTableCell() { 
     this.button = new Button("View Info"); 
     button.setOnAction(event -> { 
      Model model = (Model) getTableRow().getItem(); 
      Dialog<SubModel> dialog = new AmountDialog(model.getSubModel()); 
      dialog.getDialogPane().getButtonTypes().add(ButtonType.OK); 
      dialog.showAndWait().ifPresent(result -> { 
       model.amountProperty().set(result.getxAmount() + result.getyAmount()); 
      }); 
     }); 
    } 

    @Override protected void updateItem(T item, boolean empty) { 
     super.updateItem(item, empty); 
     setText(null); 
     if (empty) { 
      setGraphic(null); 
     } else { 
      setGraphic(button); 
     } 
    } 
} 

Dialog:

public class AmountDialog extends Dialog<SubModel> { 

    public AmountDialog(SubModel model) { 
     setTitle("Title"); 
     setHeaderText("Header"); 
     getDialogPane().setContent(createContent(model)); 
     setResultConverter(button -> { 
      if (button == ButtonType.OK) { 
       return model; 
      } 
      return null; 
     }); 
    } 

    private GridPane createContent(SubModel model) { 
     GridPane grid = new GridPane(); 
     grid.setHgap(20); 
     grid.setVgap(10); 
     grid.setPadding(new Insets(20, 20, 10, 10)); 
     Label sub = new Label("Sub"); 
     Label amount = new Label("Amount"); 

     Label amountX = new Label("AmountX"); 
     Label amountY = new Label("AmountY"); 

     TextField tfAmounX = new TextField(String.valueOf(model.getxAmount())); 
     TextField tfAmountY = new TextField(String.valueOf(model.getyAmount())); 

     Bindings.bindBidirectional(tfAmounX.textProperty(), model.xAmountProperty(), new StringDoubleConverter()); 
     Bindings.bindBidirectional(tfAmountY.textProperty(), model.yAmountProperty(), new StringDoubleConverter()); 

     grid.add(sub, 0, 0); 
     grid.add(amount, 1, 0); 

     grid.add(amountX, 0, 1); 
     grid.add(tfAmounX, 1, 1); 

     grid.add(amountY, 0, 2); 
     grid.add(tfAmountY, 1, 2); 

     return grid; 
    } 

    private class StringDoubleConverter extends StringConverter<Number> { 
     @Override public String toString(Number object) { 
      return object.toString(); 
     } 

     @Override public Number fromString(String string) { 
      // You may handle NumberFormatException. 
      return Double.valueOf(string); 
     } 
    } 
} 

Und der Controller:

public class Controller implements Initializable { 

    @FXML 
    private TableView<Model> table; 
    @FXML 
    private TableColumn<Model, Long> colId; 
    @FXML 
    private TableColumn<Model, Double> colAmount; 
    @FXML 
    private TableColumn<Model, SubModel> colInfo; 

    @Override 
    public void initialize(URL location, ResourceBundle resources) { 
     setupTable(); 
     setupData(); 
    } 

    private void setupTable() { 
     colId.setCellValueFactory(data -> data.getValue().idProperty().asObject()); 
     colAmount.setCellValueFactory(data -> data.getValue().amountProperty().asObject()); 
     colInfo.setCellValueFactory(data -> data.getValue().subModelProperty()); 

     colInfo.setCellFactory(factory -> new ButtonTableCell<>()); 
    } 

    private void setupData() { 
     SubModel firstSubModel = new SubModel(1L, 0D, 0D); 
     SubModel secondSubModel = new SubModel(2L, 0D, 0D); 

     Model fist = new Model(1L, 0D, firstSubModel); 
     Model second = new Model(2L, 0D, secondSubModel); 

     ObservableList<Model> tableData = FXCollections.observableArrayList(); 
     tableData.add(fist); 
     tableData.add(second); 

     table.setItems(tableData); 
    } 
} 

Ich weiß ein wenig lange Antwort, aber ich hoffe, es ist Ihr Problem löst. Für mich arbeitet es an der OK-Taste, aber Sie können noch weiter gehen, wenn Sie sofort aktualisieren möchten, wenn Sie den Wert in einem beliebigen Feld des Dialogs ändern.

+0

Danke. Es funktionierte :) –

Verwandte Themen