CellEditEvent редактировать соседнюю ячейку - PullRequest
0 голосов
/ 16 марта 2020

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

package application;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;

public class Main extends Application {
    private TableView<Member> table = new TableView<>();
    //private final ObservableList<Member> data = FXCollections.observableArrayList();
    @SuppressWarnings("unchecked")
    @Override

    public void start(Stage primaryStage) {
        try {
            //Definition Layout-Container
            GridPane primarygridpane = new GridPane();
            primarygridpane.setHgap(10);
            primarygridpane.setVgap(10);
            primarygridpane.setPadding(new Insets(5, 5, 5, 5));
            HBox hboxTable = new HBox(10);
            hboxTable.setPadding(new Insets(5, 5, 5, 5));
            Text titel = new Text("Application");
            titel.setFont(Font.font("Arial", FontWeight.BOLD, 28));

            // Spalte Name mit Edit-Funktion
            TableColumn<Member, String> memberColumn = new TableColumn<>("Name");
            memberColumn.setMinWidth(150);
            memberColumn.setCellValueFactory(new PropertyValueFactory<Member, String>("member"));
            memberColumn.setCellFactory(TextFieldTableCell.forTableColumn());
            memberColumn.setOnEditCommit(
                    new EventHandler<CellEditEvent<Member, String>>() {
                        @Override
                        public void handle(CellEditEvent<Member, String> t) {
                            ((Member) t.getTableView().getItems().get(t.getTablePosition().getRow())).setMember(t.getNewValue());
                        }
                    }
            );

            // Spalte Ausgaben mit Edit-Funktion
            TableColumn<Member, String> expensesColumn = new TableColumn<>("Ausgaben");
            expensesColumn.setMinWidth(50);
            expensesColumn.setCellValueFactory(new PropertyValueFactory<>("expenses"));
            expensesColumn.setCellFactory(TextFieldTableCell.forTableColumn());
            expensesColumn.setOnEditCommit(
                    new EventHandler<CellEditEvent<Member, String>>() {
                        @Override
                        public void handle(CellEditEvent<Member, String> t) {
                            ((Member) t.getTableView().getItems().get(t.getTablePosition().getRow())).setExpenses(t.getNewValue());
                            //((Member) t.getTableView().getItems().get(t.getTablePosition().getRow())).setDifference(Double.parseDouble(t.getNewValue()));
                        }
                    }
            );

            // Spalte Pfand mit Edit-Funktion
            TableColumn<Member, String> earningsColumn = new TableColumn<>("Pfand");
            earningsColumn.setMinWidth(50);
            earningsColumn.setCellValueFactory(new PropertyValueFactory<Member, String>("earnings"));
            earningsColumn.setCellFactory(TextFieldTableCell.forTableColumn());
            earningsColumn.setOnEditCommit(
                    new EventHandler<CellEditEvent<Member, String>>() {
                    @Override
                    public void handle(CellEditEvent<Member, String> t) {
                        ((Member) t.getTableView().getItems().get(t.getTablePosition().getRow())).setEarnings(t.getNewValue());
                        }
                    }
            );

            //Spalte Differenz ohne Edit-Funktion
            TableColumn<Member, Double> differenceColumn = new TableColumn<>("Differenz");
            differenceColumn.setMinWidth(50);
            differenceColumn.setCellValueFactory(new PropertyValueFactory<>("difference"));

            //Editier-Leiste
            TextField tfMember = new TextField();
            tfMember.setMinWidth(150);
            tfMember.setPromptText("Name");

            TextField tfExpenses = new TextField();
            tfExpenses.setMinWidth(50);
            tfExpenses.setPromptText("Ausgaben");

            TextField tfEarnings = new TextField();
            tfEarnings.setMinWidth(50);
            tfEarnings.setPromptText("Pfand");

            Button btnAdd = new Button("Hinzufügen");
            Button btnDelete = new Button("Löschen");
            hboxTable.getChildren().addAll(tfMember, tfExpenses, tfEarnings, btnAdd, btnDelete);

            // Spalten der Tabelle hinzufügen und Tabelle editierbar machen
            table.getColumns().addAll(memberColumn, expensesColumn, earningsColumn, differenceColumn);
            table.setEditable(true);
            // table.setItems(data);

            btnAdd.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent e) {
                    try {
                        Member member = new Member();
                        member.setMember(tfMember.getText());
                        member.setExpenses(tfExpenses.getText());
                        member.setEarnings(tfEarnings.getText());
                        member.setDifference(Double.parseDouble(tfExpenses.getText()) - Double.parseDouble(tfEarnings.getText()));
                        table.getItems().add(member);
                        //data.add(member);
                        tfMember.clear();
                        tfExpenses.clear();
                        tfEarnings.clear();
                    } catch (NumberFormatException Exception) {}
                }
            });

            //Elemente dem Gridpane hinzufügen und Rest
            primarygridpane.add(titel, 0, 0, 2, 1);
            primarygridpane.add(table, 0, 2);
            primarygridpane.add(hboxTable, 0, 3);
            Scene scene = new Scene(primarygridpane,450,550);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {e.printStackTrace();}
    }

    public static void main(String[] args) {
        launch(args);
    }
}

package application;

import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Member {
    private SimpleStringProperty member = new SimpleStringProperty();
    private DoubleProperty expenses = new SimpleDoubleProperty();
    private DoubleProperty earnings = new SimpleDoubleProperty();
    private DoubleProperty difference = new SimpleDoubleProperty();

    public Member(String member, Double expenses, Double earnings) {
        this.member = new SimpleStringProperty(member);
        this.expenses = new SimpleDoubleProperty(expenses);
        this.earnings = new SimpleDoubleProperty(earnings);
        // NumberBinding nb = earningsProperty().subtract(expensesProperty());
        // this.difference = new SimpleDoubleProperty(nb);
        // NumberBinding nb2 = expenses.subtract(earnings);

    }

    public String getMember() {
        return member.get();
    }
    public void setMember(String name) {
        member.set(name);
    }
    public final StringProperty MemberProperty(){
        return member;
    }



    public Double getExpenses() {
        return expenses.get();
    }
    public void setExpenses(Double value) {
        expenses.set(value);
    }
    public final DoubleProperty expensesProperty(){
        return expenses;
    }


    public Double getEarnings() {
        return earnings.get();
    }
    public void setEarnings(Double value) {
        earnings.set(value);
    }
    public final DoubleProperty earningsProperty(){
        return earnings;
    }



    public Double getDifference() {
        return difference.get();
    }
    public void setDifference(Double value) {
        difference.set(value);
    }
    public final DoubleProperty differenceProperty(){
        return difference;
    }
}

Я был бы очень благодарен, если бы кто-то мог мне помочь:)

1 Ответ

0 голосов
/ 17 марта 2020

Вы можете установить sh привязку, просто вызвав bind(...) в свойстве конструктора.

Поскольку differenceProperty() привязан, вызов setDifference() вызовет исключение, поэтому вы должны опустить этот метод 1 . Вы можете сделать:

public class Member {
    private StringProperty member = new SimpleStringProperty();
    private DoubleProperty expenses = new SimpleDoubleProperty();
    private DoubleProperty earnings = new SimpleDoubleProperty();
    private DoubleProperty difference = new SimpleDoubleProperty();

    public Member(String member, double expenses, double earnings) {
        this.member = new SimpleStringProperty(member);
        this.expenses = new SimpleDoubleProperty(expenses);
        this.earnings = new SimpleDoubleProperty(earnings);
        this.difference = new SimpleDoubleProperty();
        this.difference.bind(this.earnings.subtract(this.expenses));
    }

    public String getMember() {
        return member.get();
    }
    public void setMember(String name) {
        member.set(name);
    }
    public final StringProperty memberProperty(){
        return member;
    }



    public Double getExpenses() {
        return expenses.get();
    }
    public void setExpenses(Double value) {
        expenses.set(value);
    }
    public final DoubleProperty expensesProperty(){
        return expenses;
    }


    public Double getEarnings() {
        return earnings.get();
    }
    public void setEarnings(Double value) {
        earnings.set(value);
    }
    public final DoubleProperty earningsProperty(){
        return earnings;
    }



    public Double getDifference() {
        return difference.get();
    }
    // public void setDifference(Double value) {
    //     difference.set(value);
    // }
    public final DoubleProperty differenceProperty(){
        return difference;
    }
}

В вашей таблице столбцы earnings и expenses представляют числовые значения c, поэтому их следует вводить соответствующим образом. По причинам, объясненным в Свойства JavaFX в TableView , тип здесь должен быть Number, а не Double. Также обратите внимание, что если вы скажете своей реализации ячейки таблицы, как преобразовать String в значение (Number), которое вы хотите отобразить:

expensesColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));

, вам больше не нужно указывать onEditCommit обработчик (ячейка таблицы позаботится об обновлении).

Таким образом, ваша конфигурация таблицы упрощается до

// Spalte Name mit Edit-Funktion
TableColumn<Member, String> memberColumn = new TableColumn<>("Name");
memberColumn.setMinWidth(150);
memberColumn.setCellValueFactory(new PropertyValueFactory<Member, String>("member"));
memberColumn.setCellFactory(TextFieldTableCell.forTableColumn());


// Spalte Ausgaben mit Edit-Funktion
TableColumn<Member, Number> expensesColumn = new TableColumn<>("Ausgaben");
expensesColumn.setMinWidth(50);
expensesColumn.setCellValueFactory(new PropertyValueFactory<>("expenses"));
expensesColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));

// Spalte Pfand mit Edit-Funktion
TableColumn<Member, Number> earningsColumn = new TableColumn<>("Pfand");
earningsColumn.setMinWidth(50);
earningsColumn.setCellValueFactory(new PropertyValueFactory<>("earnings"));
earningsColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));

//Spalte Differenz ohne Edit-Funktion
TableColumn<Member, Number> differenceColumn = new TableColumn<>("Differenz");
differenceColumn.setMinWidth(50);
differenceColumn.setCellValueFactory(new PropertyValueFactory<>("difference"));

Обратите внимание, что NumberStringConverter имеет довольно упрощенная c реализация; Вы можете захотеть реализовать свой собственный StringConverter<Number> для поддержки, например, анализа строк на основе локали.

Также обратите внимание, что класс модели Member теперь гарантирует, что difference всегда является разницей между earnings и expenses, поэтому вам не нужно (и нельзя) установить difference свойство:

    btnAdd.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent e) {
            try {
                Member member = new Member(tfMember.getText(),
                        Double.parseDouble(tfExpenses.getText()),
                        Double.parseDouble(tfEarnings.getText()));
                table.getItems().add(member);
                tfMember.clear();
                tfExpenses.clear();
                tfEarnings.clear();
            } catch (NumberFormatException Exception) {}
        }
    });

Так же, как примечание, использование PropertyValueFactory на самом деле не является необходимым с Java 8, и потому что оно основано на отражении, склонно к молчанию, если, например, имя свойства набрано неправильно (оно также немного неэффективно). Вы можете предпочесть реализацию обратного вызова напрямую с помощью лямбда-выражения:

    // memberColumn.setCellValueFactory(new PropertyValueFactory<Member, String>("member"));
    memberColumn.setCellValueFactory(cellData -> cellData.getValue().memberProperty());

    // expensesColumn.setCellValueFactory(new PropertyValueFactory<>("expenses"));
    expensesColumn.setCellValueFactory(cellData -> cellData.getValue().expensesProperty());


    // earningsColumn.setCellValueFactory(new PropertyValueFactory<>("earnings"));
    earningsColumn.setCellValueFactory(cellData -> cellData.getValue().earningsProperty());

Этот подход также позволяет полностью исключить свойство difference из класса Member и просто использовать привязку непосредственно в столбце таблицы. :

    differenceColumn.setCellValueFactory(cd -> 
        cd.getValue().earningsProperty().subtract(cd.getValue().expensesProperty()));

Выбор здесь зависит в основном от того, считаете ли вы difference неотъемлемой частью данных (поэтому его следует включить в модель), или же данные просто состоит из earnings и expenses, а difference - это просто то, что вы хотите визуализировать в таблице.


(1) На самом деле, вы должны использовать ReadOnlyProperty здесь для представления difference, поскольку вызов differenceProperty().set(...) также вызовет исключение с кодом так, как он написан. В основном:

private final ReadOnlyDoubleWrapper difference = new ReadOnlyDoubleWrapper() ;

// Constructor and other properties as before ...

public final ReadOnlyDoubleProperty differenceProperty() {
     return difference.getReadOnlyProperty();
}

public final double getDifference() {
    return differenceProperty().get();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...