【问题标题】:Use a listener to get selected rows (mails) in tableView and add mails to my list of mails使用侦听器在 tableView 中获取选定的行(邮件)并将邮件添加到我的邮件列表中
【发布时间】:2021-12-11 03:56:41
【问题描述】:

我有一个 id、name、mail、phone 和 select 的 tableView。 我想获取我的 tableView 的所有选定行并将选定的邮件添加到我的模型类中的电子邮件列表中。 我读到:this 但我不知道如何将侦听器添加到我的 obsevealList 数据中以获得我想要的作为数据的侦听器:

for (People p : model.getData()) {
           if (p.getCheck())
               model.getEmails().add(p.getMail());
       }
   }

请问我该怎么做?

人物类:

 public class People {
 private IntegerProperty id = new SimpleIntegerProperty(this, "id");
 private StringProperty name = new SimpleStringProperty(this, "name");
 private StringProperty mail = new SimpleStringProperty(this, "mail");
 private StringProperty phone = new SimpleStringProperty(this, "phone");
 private BooleanProperty check = new SimpleBooleanProperty(this, "check");


 public People(String name, String mail, String phone) {
     this.name.set(name);
     this.mail.set(mail);
     this.phone.set(phone);
     this.check.set(false);
 }
 public People(Integer id,String name, String mail, String phone) {
     this.id.set(id);
     this.name.set(name);
     this.mail.set(mail);
     this.phone.set(phone);
     this.check.set(false);
 }
 public IntegerProperty idProperty() {
     return id;
 }

 public Integer getId() {
     return idProperty().get();
 }

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

 public void setName(String name) {
     nameProperty().set(name);
 }
 public StringProperty mailProperty() {
     return mail;
 }
 public String getMail() {
     return mailProperty().get();
 }

 public void setMail(String mail) {
     mailProperty().set(mail);
 }

 public StringProperty phoneProperty() {
     return phone;
 }
 public String getphone() {
     return phoneProperty().get();
 }

 public void setPhone(String phone) {
     phoneProperty().set(phone);
 }
 public BooleanProperty checkProperty() {
     return check;
 }

 public Boolean getCheck() {
     return checkProperty().get();
 }

 public void setCheck(Boolean remark) {
     checkProperty().set(remark);
}
}

我的数据类:

public class Data {
    private Connection con = null;
    private PreparedStatement ps = null;
    private Statement st = null;
    private ResultSet rs = null;
    private List<String> emails;
    private String mychoice;

    private ObservableList<People> data = FXCollections.observableArrayList();
    public  ObservableList<People> getData(){
        return data;
    }

    public List<String> getEmails() {
        return emails;
    }

    public void addEmails(String mail) {
        emails.add(mail);
    }

    public void setEmails(List<String> emails) {
        this.emails = emails;
    }

    public String getMychoice() {
        return mychoice;
    }

   public void setMychoice(String mychoice) {
        this.mychoice = mychoice;
    }

    
    public void loadData() {
        try {
            con=getConnect();
            data.clear();
            String query = "SELECT * FROM " + mychoice;
            ps = con.prepareStatement(query);
            rs = ps.executeQuery();
            while (rs.next()) {
                data.add(new People(
                        rs.getInt(1),
                        rs.getString(2),
                        rs.getString(3),
                        rs.getString(4)
                ));
            }
            rs.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

Show 类的特征:

public class Show implements Initializable {
  
    Data model=new Data();
    @FXML
    private TableView<People> table;
    @FXML
    private TableColumn<People, String> name,mail, phone;
    @FXML
    private TableColumn<People, Integer> id;
    @FXML
    private TableColumn <People,Boolean> select;

    @Override

    public void initialize(URL url, ResourceBundle resourceBundle) {
        con = getConnect();
        loadTable(); //set all table name inside the combobox
        table.setItems(model.getData());
        id.setCellValueFactory(cd -> cd.getValue().idProperty().asObject());
        name.setCellValueFactory(cd -> cd.getValue().nameProperty());
        phone.setCellValueFactory(cd -> cd.getValue().phoneProperty());
        mail.setCellValueFactory(cd -> cd.getValue().mailProperty());
        table.setEditable(true);
        name.setCellFactory(TextFieldTableCell.forTableColumn());
        mail.setCellFactory(TextFieldTableCell.forTableColumn());
        phone.setCellFactory(TextFieldTableCell.forTableColumn());
        select.setCellFactory(CheckBoxTableCell.forTableColumn(select));
        select.setCellValueFactory(cd -> cd.getValue().checkProperty());

 select.setCellFactory(CheckBoxTableCell.forTableColumn(new Callback<Integer, ObservableValue<Boolean>>() {

            @Override
            public ObservableValue<Boolean> call(Integer param) {
                System.out.println("Contact " + model.getData().get(param).getMail() + " changed value to " + model.getData().get(param).getCheck());
                return model.getData().get(param).checkProperty();
            }
        }));

            
    }
}

【问题讨论】:

    标签: java javafx


    【解决方案1】:

    此解决方案是trashgod's solution 的变体,我知道此代码并非完全基于您问题中提供的代码,但我认为这些概念应该能够应用到您的应用程序中,而不会带来太多额外的困难。

    使用过滤列表

    此解决方案添加了一个示例,将 TableView 中的选定项目反映到单独的后备列表中。

    单独的后备列表定义为FilteredList,仅基于完整列表中的选定值。过滤列表使用谓词来确定原始列表中的项目是否应反映在过滤列表中。谓词基于复选框选择状态。

    private final ObservableList<Person> mailingList = new FilteredList<>(
            people,
            person -> person.invitedProperty().get()
    );
    

    FilteredList 仍然需要提取器才能工作(在这种情况下)

    为了实时更新过滤后的列表,使用了一个提取器(如在垃圾神解决方案中)。这是必需的,因为过滤后的列表需要观察原始列表的变化。

    person -> new Observable[] { person.invitedProperty() }
    

    通常,只有在原始列表中添加或删除项目时才会触发对原始列表的更改。但是,当我们仅更改支持复选框的布尔属性时,它只是更新项目属性以启动更改,而不是从原始列表中添加或删除整个项目。要注册此更改,需要列出可观察属性的提取器,该属性可以为原始列表启动适当的更改通知(基于对支持复选框的属性的更改)。

    这与垃圾神解决方案一样。我只是内联了这个解决方案中的一些定义,但整体机制是一样的。

    最初,在开发此解决方案时,我认为可能不需要提取器。但是,为了获得实时更改,FilteredList 实现依赖于观察原始列表中的适当更改。对于这个例子,它改变列表中已有对象的值,而不是从列表中添加或删除对象,如果没有提取器,这是不可能的。

    示例解决方案

    在应用程序中我定义了两个视图,一个是用于选择要添加到邮件列表的人员的 TableView。另一个是由 FilteredList 支持的 ListView,仅显示邮件列表中的人员。当用户在表格视图中选中和取消选中项目时,更改会立即反映在由过滤列表支持的邮件列表视图中。

    import javafx.application.Application;
    import javafx.beans.Observable;
    import javafx.beans.property.*;
    import javafx.collections.*;
    import javafx.collections.transformation.FilteredList;
    import javafx.geometry.Insets;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.control.cell.CheckBoxTableCell;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    import java.util.Arrays;
    
    /**
     * https://stackoverflow.com/a/69712532/230513
     */
    public class MailingListViewApp extends Application {
    
        private final ObservableList<Person> people = FXCollections.observableList(
                Arrays.asList(
                        new Person("Ralph", "Alpher", true, "ralph.alpher@example.com"),
                        new Person("Hans", "Bethe", false, "hans.bethe@example.com"),
                        new Person("George", "Gammow", true, "george.gammow@example.com"),
                        new Person("Paul", "Dirac", false, "paul.dirac@example.com"),
                        new Person("Albert", "Einstein", true, "albert.einstein@example.com")
                ),
                person -> new Observable[] { person.invitedProperty() }
        );
    
        private final ObservableList<Person> mailingList = new FilteredList<>(
                people,
                person -> person.invitedProperty().get()
        );
    
        public static void main(String[] args) {
            launch(args);
        }
    
        @Override
        public void start(Stage stage) {
            final TableView<Person> mailingListSelectionTableView =
                    createMailingListSelectionTableView();
    
            ListView<Person> mailingListView =
                    createMailingListView();
    
            final VBox vbox = new VBox(
                    10,
                    new TitledPane("Candidates", mailingListSelectionTableView),
                    new TitledPane("Mailing List", mailingListView)
            );
            vbox.setPadding(new Insets(10));
    
            stage.setScene(new Scene(vbox));
            stage.show();
        }
    
        private TableView<Person> createMailingListSelectionTableView() {
            final TableView<Person> selectionTableView = new TableView<>(people);
            selectionTableView.setPrefSize(440, 180);
    
            TableColumn<Person, String> firstName = new TableColumn<>("First Name");
            firstName.setCellValueFactory(cd -> cd.getValue().firstNameProperty());
            selectionTableView.getColumns().add(firstName);
    
            TableColumn<Person, String> lastName = new TableColumn<>("Last Name");
            lastName.setCellValueFactory(cd -> cd.getValue().lastNameProperty());
            selectionTableView.getColumns().add(lastName);
    
            TableColumn<Person, Boolean> invited = new TableColumn<>("Invited");
            invited.setCellValueFactory(cd -> cd.getValue().invitedProperty());
            invited.setCellFactory(CheckBoxTableCell.forTableColumn(invited));
            selectionTableView.getColumns().add(invited);
    
            TableColumn<Person, String> email = new TableColumn<>("Email");
            email.setCellValueFactory(cd -> cd.getValue().emailProperty());
            selectionTableView.getColumns().add(email);
    
            selectionTableView.setEditable(true);
            return selectionTableView;
        }
    
        private ListView<Person> createMailingListView() {
            ListView<Person> mailingListView = new ListView<>();
            mailingListView.setCellFactory(param -> new ListCell<>() {
                @Override
                protected void updateItem(Person item, boolean empty) {
                    super.updateItem(item, empty);
    
                    if (empty || item == null || item.emailProperty().get() == null) {
                        setText(null);
                    } else {
                        setText(item.emailProperty().get());
                    }
                }
            });
            mailingListView.setItems(mailingList);
            mailingListView.setPrefHeight(160);
            return mailingListView;
        }
    
        private static class Person {
    
            private final StringProperty firstName;
            private final StringProperty lastName;
            private final BooleanProperty invited;
            private final StringProperty email;
    
            private Person(String fName, String lName, boolean invited, String email) {
                this.firstName = new SimpleStringProperty(fName);
                this.lastName = new SimpleStringProperty(lName);
                this.invited = new SimpleBooleanProperty(invited);
                this.email = new SimpleStringProperty(email);
            }
    
            public StringProperty firstNameProperty() {
                return firstName;
            }
    
            public StringProperty lastNameProperty() {
                return lastName;
            }
    
            public BooleanProperty invitedProperty() {
                return invited;
            }
    
            public StringProperty emailProperty() {
                return email;
            }
        }
    }
    

    FilteredList 解决方案适合您吗?

    很难说。这是一种潜在的方法。

    正如 cmets 中的垃圾神所说,您的应用程序的最佳方法可能取决于您的整体 UI 和应用程序架构。

    例如,您可以使用保存按钮或其他提交点,而不是使用过滤列表,在此它会检查垃圾标志解决方案中的脏标志,例如 modelChanged 标志(甚至取消侦听器/模型完全改变了概念)。然后只需遍历原始列表,提取在该时间点选择的任何项目,并将这些值反映在您的模型中(例如,提交到数据库)。这将是一种 MVVM 方法,其中可供选择的可观察列表仅作为视图模型 (VM) 参与,而提交的邮件信息的单独列表是模型 (M)。

    即使您确实使用了上述单独的保存点方法,也许添加过滤列表仍然可以为您简化转换任务,因为您需要做的就是查看过滤列表中的所有项目。

    此外,过滤后的列表可以用于应用程序的其他功能,例如此解决方案中显示的邮件列表视图。

    【讨论】:

    • FilteredList的即时传播非常吸引人;我同意你对上下文的阐述。
    • @jewelsea 非常感谢,我只是在学习新概念。我实际上有一个问题我应该在哪里使用提取器。就我而言,我想获取选定邮件的列表以在其他控制器中调用它。我绑定在我的数据列表private ObservableList&lt;People&gt; data = FXCollections.observableArrayList(p-&gt; new Observable[]{ p.checkProperty()}); 中提取数据,但如果我这样做,我将无法收到邮件。
    • 最好问一个关于提取器使用的新问题。通过这种方式,您可以提供有针对性的minimal reproducible example,并提供格式良好的代码和清晰、重点突出的问题描述,可以由各种人回答并为其他人提供可搜索的解决方案。在您的新问题中,您可以参考这个问题并提供任何其他相关文档、javadoc 或研究的链接。
    【解决方案2】:

    正如here 所述,从这个相关的完整example 开始,我为包含相关BooleanPropertynew Observable[]{invited}Observable[] 创建了一个带有extractorObservableList

    ObservableList<Person> model =
        FXCollections.observableArrayList(p -> p.extract());
    

    然后添加监听器:

    model.addListener(createListener());
    ListChangeListener<Person> createListener() {
    return (Change<? extends Person> c) -> {
        while (c.next()) {
            modelChanged = true:
        }
    };
    

    这种方法有条件地将模型标记为已更改。以后如果modelChanged,就可以遍历ObservableList来收集需要的数据并清除flag。

    为了查看效果,下面的变体更新了一个标签以显示当前的选择状态。

    import java.util.Arrays;
    import javafx.application.Application;
    import javafx.beans.Observable;
    import javafx.beans.property.BooleanProperty;
    import javafx.beans.property.SimpleBooleanProperty;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.collections.FXCollections;
    import javafx.collections.ListChangeListener;
    import javafx.collections.ListChangeListener.Change;
    import javafx.collections.ObservableList;
    import javafx.geometry.Insets;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.control.TableColumn;
    import javafx.scene.control.TableView;
    import javafx.scene.control.cell.CheckBoxTableCell;
    import javafx.scene.control.cell.TextFieldTableCell;
    import javafx.scene.layout.VBox;
    import javafx.scene.text.Font;
    import javafx.stage.Stage;
    
    /**
     * https://stackoverflow.com/a/69715448/230513
     * https://stackoverflow.com/q/69711881/230513
     * https://docs.oracle.com/javase/8/javafx/user-interface-tutorial/table-view.htm
     * https://docs.oracle.com/javase/8/javafx/properties-binding-tutorial/binding.htm#JFXBD107
     * https://stackoverflow.com/a/38050982/230513
     * https://stackoverflow.com/a/25204271/230513
     */
    public class TableViewTest extends Application {
    
        private final ObservableList<Person> model = FXCollections.observableList(
            Arrays.asList(
                new Person("Ralph", "Alpher", true, "ralph.alpher@example.com"),
                new Person("Hans", "Bethe", false, "hans.bethe@example.com"),
                new Person("George", "Gammow", true, "george.gammow@example.com"),
                new Person("Paul", "Dirac", false, "paul.dirac@example.com"),
                new Person("Albert", "Einstein", true, "albert.einstein@example.com")
            ), p -> p.extract());
    
        public static void main(String[] args) {
            launch(args);
        }
    
        @Override
        public void start(Stage stage) {
            stage.setTitle("Table View Sample");
            stage.setWidth(640);
            stage.setHeight(480);
    
            final TableView<Person> table = new TableView<>(model);
            final Label label = new Label("Address Book");
            label.setFont(Font.font("Serif", 20));
            final Label status = new Label(calcuateStatus());
    
            TableColumn<Person, String> firstName = new TableColumn<>("First Name");
            firstName.setCellValueFactory(cd -> cd.getValue().firstNameProperty());
            table.getColumns().add(firstName);
    
            TableColumn<Person, String> lastName = new TableColumn<>("Last Name");
            lastName.setCellValueFactory(cd -> cd.getValue().lastNameProperty());
            table.getColumns().add(lastName);
    
            TableColumn<Person, Boolean> invited = new TableColumn<>("Invited");
            invited.setCellValueFactory(cd -> cd.getValue().invitedProperty());
            invited.setCellFactory(CheckBoxTableCell.forTableColumn(invited));
            table.getColumns().add(invited);
    
            TableColumn<Person, String> email = new TableColumn<>("Email");
            email.setCellValueFactory(cd -> cd.getValue().emailProperty());
            email.setCellFactory(TextFieldTableCell.forTableColumn());
            email.setOnEditCommit(t -> t.getRowValue().emailProperty().set(t.getNewValue()));
            table.getColumns().add(email);
    
            model.addListener(createListener(status));
            table.setEditable(true);
            table.setTableMenuButtonVisible(true);
    
            final VBox vbox = new VBox();
            vbox.setSpacing(8);
            vbox.setPadding(new Insets(8));
            vbox.getChildren().addAll(label, table, status);
    
            stage.setScene(new Scene(vbox));
            stage.show();
        }
    
        private String calcuateStatus() {
            int sum = 0;
            for (Person p : model) {
                if (p.invited.get()) {
                    sum += 1;
                }
            }
            return sum + " / " + model.size() + " invited.";
        }
    
        private ListChangeListener<Person> createListener(Label status) {
            return (Change<? extends Person> c) -> {
                while (c.next()) {
                    status.setText(calcuateStatus());
                }
            };
        }
    
        private static class Person {
    
            private final StringProperty firstName;
            private final StringProperty lastName;
            private final BooleanProperty invited;
            private final StringProperty email;
    
            private Person(String fName, String lName, boolean invited, String email) {
                this.firstName = new SimpleStringProperty(fName);
                this.lastName = new SimpleStringProperty(lName);
                this.invited = new SimpleBooleanProperty(invited);
                this.email = new SimpleStringProperty(email);
            }
    
            public StringProperty firstNameProperty() {
                return firstName;
            }
    
            public StringProperty lastNameProperty() {
                return lastName;
            }
    
            public BooleanProperty invitedProperty() {
                return invited;
            }
    
            public StringProperty emailProperty() {
                return email;
            }
    
            public Observable[] extract() {
                return new Observable[]{invited};
            }
        }
    }
    

    【讨论】:

    • @Lamia 我不认为你的评论是完整的 "I want for" 。 . .什么?
    • 非常感谢您的努力,但我想要的不仅仅是知道是否有人选择了一个复选框(我可以从我的代码的最后一部分```select.setCellFactory``` ) 或计算选择的数量(正如您编写的代码可以提供的那样)。 **我想为每个选定的人获取他的邮件,将其添加到我的电子邮件列表中(在我的数据类中声明)** 你能再读一遍我的代码吗?
    • @jewelsea soory 我的评论未完成
    • @Lamia:你的问题是如何添加一个能看到Change的监听器;该示例在独立的上下文中说明了这一点;在用户完成选择复选框之前,我不会构建List,就像提交表单时一样;您的确切解决方案将取决于您的应用程序的设计。
    猜你喜欢
    • 2013-04-26
    • 2017-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-02
    • 2017-11-06
    • 1970-01-01
    • 2014-04-03
    相关资源
    最近更新 更多