【问题标题】:Get Checkbox value in a table in JavaFX在JavaFX中的表中获取复选框值
【发布时间】:2014-01-02 08:47:17
【问题描述】:

我有一个表格,它有一列带有复选框。

单击按钮时,我想找出哪些复选框被选中,哪些未被选中。 到目前为止,我设法在表格中创建了复选框。 代码如下。

public class TTEs implements Initializable {

    @FXML
    private TableView<TestObject> tableReport;

    @FXML
    private TableColumn<TestObject, String> name;

    @FXML
    private TableColumn<TestObject, Boolean> checkbox;

    @FXML
    public void getValues() {        
        //the method will get what check boxes are checked (this part is the problem)
    }

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) { 
        ObservableList<TestObject> data = FXCollections.observableArrayList();
        data.add(new TestObject("Test 1", true));
        data.add(new TestObject("Test 2", false));

        tableReport.setItems(data);

        name.setCellValueFactory(new PropertyValueFactory<TestObject, String>("name"));
        checkbox.setCellValueFactory(new PropertyValueFactory<TestObject, Boolean>("checked"));

        checkbox.setCellFactory(new Callback<TableColumn<TestObject, Boolean>,
            TableCell<TestObject, Boolean>>() {

            public TableCell<TestObject, Boolean> call(TableColumn<TestObject, Boolean> p) {
                return new CheckBoxTableCell<TestObject, Boolean>();
            }
        });
    }

    //CheckBoxTableCell for creating a CheckBox in a table cell
    public static class CheckBoxTableCell<S, T> extends TableCell<S, T> {
        private final CheckBox checkBox;
        private ObservableValue<T> ov;

        public CheckBoxTableCell() {
            this.checkBox = new CheckBox();
            this.checkBox.setAlignment(Pos.CENTER);

            setAlignment(Pos.CENTER);
            setGraphic(checkBox);
        } 

        @Override public void updateItem(T item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) {
                setText(null);
                setGraphic(null);
            } else {
                setGraphic(checkBox);
                if (ov instanceof BooleanProperty) {
                    checkBox.selectedProperty().unbindBidirectional((BooleanProperty) ov);
                }
                ov = getTableColumn().getCellObservableValue(getIndex());
                if (ov instanceof BooleanProperty) {
                    checkBox.selectedProperty().bindBidirectional((BooleanProperty) ov);
                }
            }
        }
    }
}

当我调试时,我发现:

        ov = getTableColumn().getCellObservableValue(getIndex());
        if (ov instanceof BooleanProperty) {
            checkBox.selectedProperty().bindBidirectional((BooleanProperty) ov);
        }

在上述情况下,它永远不会进入if 语句中,这意味着ov 不是BooleanProperty 的实例。但是当我打印ov的类时,

System.out.println(ov.getClass().getName());

它打印为

javafx.beans.property.ReadOnlyObjectWrapper

ReadOnlyObjectWrapperBooleanProperty 的子类,那么为什么instanceof 检查不起作用?

【问题讨论】:

  • Noo,那是在 java swing 中。我的应用程序正在使用 javaFX。那里有很多不同。
  • gotozero 下面的答案对我有用,并花了大约一分钟来实施。如果您使用 fxml,则无需更改代码。
  • 如果你想检查子类你可以使用if (BooleanProperty.class.isAssignableFrom(myObject.getClass()))

标签: java javafx javafx-2


【解决方案1】:

在 Java 8 上测试。
只有 4 件简单的事情。

1) 制作 CheckBoxCellFactory 类。放在项目中的某个位置。

public class CheckBoxCellFactory implements Callback {
    @Override
    public TableCell call(Object param) {
        CheckBoxTableCell<Person,Boolean> checkBoxCell = new CheckBoxTableCell();
        return checkBoxCell;
    }
}

2) 你的模型类。以人为例。

public static class Person {
   private SimpleBooleanProperty checked = new SimpleBooleanProperty(false);
   // other columns here

    public SimpleBooleanProperty checkedProperty() {
        return this.checked;
    }

    public java.lang.Boolean getChecked() {
        return this.checkedProperty().get();
    }

    public void setChecked(final java.lang.Boolean checked) {
        this.checkedProperty().set(checked);
    }

    // getter/setter for other columns

}

3) 在您的 fxml 文件中进行了修改。您的 TableView -> TableColumn 部分将如下所示:

<TableColumn fx:id="checkBoxTableColumn" maxWidth="34.0" minWidth="26.0" prefWidth="34.0" resizable="false" sortable="false">   
<cellValueFactory><PropertyValueFactory property="checked" /></cellValueFactory>
<cellFactory><partarch.fx.CheckBoxCellFactory /></cellFactory>
</TableColumn>

4) 如果您想让您的复选框可编辑

  • 4.1 在场景生成器中打开 fxml。
  • 4.2 选择复选框列,然后在“属性”窗格中选中“可编辑”属性。
  • 4.3 选择包含您的复选框的 TableView 并执行相同操作(检查“属性”窗格中的“可编辑”属性)。

SF 上的二进制文件和GitHub 上的源代码中实现

【讨论】:

  • 不起作用。当用户单击它时,复选框本身会发生变化,但值不会传递给模型
  • @Mateus Viccari ,你可以看这里GitHub
  • @gotozero 这个答案对我来说是逐字逐句的(我所做的只是按照你的指示去做)。非常感谢 gotozero 提供最直接的答案。这确实是正确的答案,值得更多的支持。
  • 该解决方案效果很好,也很优雅!我要添加小 cmets:您必须在模型中使用 BooleanProperty 而不是 ObjectProperties,您还可以通过在控制器中用以下更新替换步骤 3 来避免步骤 1:checkBoxTableColumn.setCellValueFactory(cellData -&gt; cellData.getValue().checkedProperty()); checkBoxTableColumn.setCellFactory(param -&gt; new CheckBoxTableCell&lt;Person, Boolean&gt;());
  • 有 3 种不同的回调接口。我应该实施哪一个?
【解决方案2】:

遍历TableView 的数据模型,检查绑定到每个CheckBox 的布尔值。

@FXML
public void getValues(){        
    ObservableList<TestObject> data = tableReport.getItems();

    for (TestObject item : data){
        //check the boolean value of each item to determine checkbox state
    }
}

【讨论】:

  • 试过了,但是布尔值没有映射到复选框。不知道为什么以及如何。 (虽然我将布尔值设置为 'true' 它没有选中复选框)
【解决方案3】:
package checkboxtablecelltest;

import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

/**
 *
 * @author reegan
 */
public class CheckBoxTableCellTest extends Application {

    @Override   
    public void start(Stage primaryStage) {
        final TableView<Person> tableView = new TableView<Person>();
        tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        tableView.setItems(FXCollections.observableArrayList(
                new Person("Robert", "Plant"),
                new Person("Neil", "Young"),
                new Person("Willie", "Nelson"),
                new Person("Natalie", "Merchant")));
        tableView.getItems().get(3).setVegetarian(true);
        final TableColumn<Person, String> firstNameCol = new TableColumn<Person, String>("First Name");
        final TableColumn<Person, String> lastNameCol = new TableColumn<Person, String>("Last Name");
        final TableColumn<Person, Boolean> vegetarianCol = new TableColumn<Person, Boolean>("Vegetarian");
        tableView.getColumns().addAll(firstNameCol, lastNameCol, vegetarianCol);
        firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
        lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
        vegetarianCol.setCellValueFactory(new PropertyValueFactory<Person, Boolean>("vegetarian"));
        vegetarianCol.setCellFactory(CheckBoxTableCell.forTableColumn(vegetarianCol));
        vegetarianCol.setEditable(true);
        tableView.setEditable(true);

        final BorderPane root = new BorderPane();
        root.setCenter(tableView);

        final HBox controls = new HBox(5);
        final Button infoButton = new Button("Show details");
        infoButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                for (Person p : tableView.getItems()) {
                    System.out.printf("%s %s (%svegetarian)%n", p.getFirstName(),
                            p.getLastName(), p.isVegetarian() ? "" : "not ");
                            System.out.println(tableView.getSelectionModel().getSelectedItems());

                }
                System.out.println();
            }
        });
        controls.getChildren().add(infoButton);
        root.setBottom(controls);

        Scene scene = new Scene(root, 300, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

    public static class Person {

        private StringProperty firstName;
        private StringProperty lastName;
        private BooleanProperty vegetarian;

        public Person(String firstName, String lastName) {
            this.firstName = new SimpleStringProperty(firstName);
            this.lastName = new SimpleStringProperty(lastName);
            this.vegetarian = new SimpleBooleanProperty(false);
        }

        public String getFirstName() {
            return firstName.get();
        }

        public String getLastName() {
            return lastName.get();
        }

        public boolean isVegetarian() {
            return vegetarian.get();
        }

        public void setFirstName(String firstName) {
            this.firstName.set(firstName);
        }

        public void setLastName(String lastName) {
            this.lastName.set(lastName);
        }

        public void setVegetarian(boolean vegetarian) {
            this.vegetarian.set(vegetarian);
        }

        public StringProperty firstNameProperty() {
            return firstName;
        }

        public StringProperty lastNameProperty() {
            return lastName;
        }

        public BooleanProperty vegetarianProperty() {
            return vegetarian;
        }
    }
}

`

【讨论】:

  • 不要不解释就贴一堵代码墙
  • 对不起@StealthRabbi 我的英语很差,所以我的代码中没有任何解释....之后我会写一个解释。谢谢你的建议...
【解决方案4】:

您的方法的主要问题是 CheckBoxTableCell 用法: Cell 是一种重用的“渲染”机器。如果你尝试添加一个状态,比如 CheckBox 变量,你就有麻烦了。

解决问题的最简单方法是每次都分配复选框。以下代码提供了一个有效的复选框列:

public static class Member {
    private StringProperty myName;
    private BooleanProperty myCheck;

    public Member(String name, boolean checked) {
        myName = new SimpleStringProperty(name);
        myCheck = new SimpleBooleanProperty(checked);
    }

    public StringProperty nameProperty() { return myName; }    
    public BooleanProperty checkProperty() { return myCheck; }
}

VBox testTable6(VBox box) { // check box bind to cell property
    ObservableList<Member> members = FXCollections.observableArrayList();

    members.add(new Member("peter", true));
    members.add(new Member("gernot", true));
    members.add(new Member("fritz", false));

    TableView<Member> table = new TableView<Member>();
    table.prefHeightProperty().bind(box.heightProperty());
    table.setItems(members);

    TableColumn<Member,String> c1 = new TableColumn<Member,String>("Name");
    c1.setCellValueFactory(new PropertyValueFactory<Member,String>("name"));
    table.getColumns().add(c1);

    TableColumn<Member,Boolean> c2 = new TableColumn<Member,Boolean>("Membercheck");
    c2.setCellValueFactory(new PropertyValueFactory<Member,Boolean>("check"));
    c2.setCellFactory(column -> new TableCell<Member, Boolean>(){
        public void updateItem(Boolean check, boolean empty) {
            super.updateItem(check, empty);
            if (check == null || empty) {
                setGraphic(null);
            } else {
                CheckBox box = new CheckBox();
                BooleanProperty checked = (BooleanProperty)column.getCellObservableValue(getIndex());
                box.setSelected(checked.get());
                box.selectedProperty().bindBidirectional(checked);
                setGraphic(box);
            }
        }
    }
            );
    table.getColumns().add(c2);

    box.getChildren().addAll(table);
    return box;
}

由于双向属性绑定是弱绑定,因此即使您无法显式取消绑定,carbage 收集也会正常工作。

对 BooleanProperty 的未经检查的强制转换无论如何在这里都不是一个好的样式,抱歉。 考虑使用以下方法访问整个对象:

...
Member member = table.getItems().get(getIndex());
box.setSelected(member.checkProperty().get());
box.selectedProperty().bindBidirectional(member.checkProperty());
....

顺便说一句:birdeirectional 绑定不会在那一刻设置 selected 属性,只有在之后更改它!所以显式赋值:

box.setSelected(member.checkProperty().get());

此处为必填项。

【讨论】:

    【解决方案5】:

    有点离题 - 但在 Kotlin 中你可以这样做:

        isPvtColumn.cellFactory = Callback { CheckBoxTableCell<KeyWithProperties, Boolean>() }
    
        isPvtColumn.cellValueFactory = Callback {
            a -> a.value.private
        }
    

    这就是它的全部内容

    【讨论】:

    • 对我非常有用,因为我在其他地方找不到此信息
    【解决方案6】:

    在初始化方法中,将您的自定义工厂切换为 JavaFX 提供的工厂:

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
    
         ObservableList<TestObject> data = FXCollections.observableArrayList();
         data.add(new TestObject("Test 1", true));
         data.add(new TestObject("Test 2", false));
    
         tableReport.setItems(data);
    
         name.setCellValueFactory(new PropertyValueFactory<TestObject, String>("name"));
         checkbox.setCellValueFactory(new PropertyValueFactory<TestObject, Boolean>("checked"));
    
         checkbox.setCellFactory(
             CheckBoxTableCell.forTableColumn(checkbox)
         );
    
    }
    

    现在您的数据已绑定到列,您可以遍历项目并检查“已检查”字段:

    @FXML
    public void getValues(){        
        ObservableList<TestObject> data = tableReport.getItems();
    
        for (TestObject item : data){
            //check the boolean value of each item to determine checkbox state
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-07-02
      • 2017-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-20
      • 1970-01-01
      相关资源
      最近更新 更多