带有布尔值而不是 SimpleBooleanProperty 的 JavaFX TableView CheckBoxTableCell

JavaFX TableView CheckBoxTableCell with boolean instead of SimpleBooleanProperty

我想知道如何在没有 ObservableProperty 的情况下使用 CheckBoxTableCell。我正在使用一个简单的布尔值,因为我想将数据序列化到我的 DerbyDB。

当然可以制作一个 SimpleBooleanProperty 并用 @Transient 对其进行注释并将其环绕在我的正常 boolean active 周围。

我的布尔值显示正确,但在我的 Tableview 中单击复选框后我无法存储新值。

private void setupColActive() {
    colActive.setCellValueFactory(new PropertyValueFactory<UserVillage, Boolean>("active"));
    colActive.setCellFactory(CheckBoxTableCell.forTableColumn(colActive));       
    colActive.setEditable(true);
}

我已经尝试将它与 colActive.setOnEditCancelcolActive.setOnEditCommitcolActive.setOnEditStart 结合打印轮廓,但我无法捕获新值来存储它。我想找到一种方法让它工作而不用包装成 @Transient SimpleBooleanProperty active

序列化问题不会阻止您在模型中使用 JavaFX 属性 class UserVillage。我建议使用这些属性并通过定义 readObjectwriteObject 方法来自定义序列化机制。这是一个 class 定义的演示:

public class Item implements Serializable {
    private StringProperty name ;
    private IntegerProperty value ;
    private BooleanProperty active ;

    public Item(String name, int value, boolean active) {
        this.name = new SimpleStringProperty(name) ;
        this.value = new SimpleIntegerProperty(value);
        this.active = new SimpleBooleanProperty(active);
    }


    public final StringProperty nameProperty() {
        return this.name;
    }
    public final java.lang.String getName() {
        return this.nameProperty().get();
    }
    public final void setName(final java.lang.String name) {
        this.nameProperty().set(name);
    }
    public final IntegerProperty valueProperty() {
        return this.value;
    }
    public final int getValue() {
        return this.valueProperty().get();
    }
    public final void setValue(final int value) {
        this.valueProperty().set(value);
    }
    public final BooleanProperty activeProperty() {
        return this.active;
    }
    public final boolean isActive() {
        return this.activeProperty().get();
    }
    public final void setActive(final boolean active) {
        this.activeProperty().set(active);
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeObject(getName());
        out.writeInt(getValue());
        out.writeBoolean(isActive());
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        this.name = new SimpleStringProperty((String)in.readObject());
        this.value = new SimpleIntegerProperty(in.readInt());
        this.active = new SimpleBooleanProperty(in.readBoolean());
    }

    // Quick test:
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Item testItem = new Item("Item", 42, true);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(testItem);
        oos.close();
        byte[] bytes = out.toByteArray();

        ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
        Item result = (Item) in.readObject();
        System.out.println(result.getName());
        System.out.println(result.getValue());
        System.out.println(result.isActive());
    }
}

使用这种方法,CheckBoxTableCell 将自动绑定到 PropertyValueFactory 返回的 属性。

另一种方法是创建一个 BooleanProperty 专门用于绑定到选中状态的复选框。请注意,根据 Javadocs,因为 CheckBoxTableCell 永远不会进入或离开编辑状态,所以永远不会调用编辑回调 onEditCommit 等:

Note that the CheckBoxTableCell renders the CheckBox 'live', meaning that the CheckBox is always interactive and can be directly toggled by the user. This means that it is not necessary that the cell enter its editing state (usually by the user double-clicking on the cell). A side-effect of this is that the usual editing callbacks (such as on edit commit) will not be called. If you want to be notified of changes, it is recommended to directly observe the boolean properties that are manipulated by the CheckBox.

下面是使用此技术的演示。同样,我真的不推荐这种方法,因为您最终会创建一堆 BooleanProperty,这些很快就有资格进行垃圾回收。预期的方法是让模型使用 JavaFX 属性。

import java.util.stream.IntStream;

import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
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.stage.Stage;

public class TableViewCheckBoxTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        TableView<Item> table = new TableView<>();
        table.setEditable(true);
        table.getColumns().add(createColumn("Name", "name"));
        table.getColumns().add(createColumn("Value", "value"));

        TableColumn<Item, Boolean> activeCol = createColumn("Active", "active");
        table.getColumns().add(activeCol);

        activeCol.setCellFactory(col -> {
            CheckBoxTableCell<Item, Boolean> cell = new CheckBoxTableCell<>(index -> {
                BooleanProperty active = new SimpleBooleanProperty(table.getItems().get(index).isActive());
                active.addListener((obs, wasActive, isNowActive) -> {
                    Item item = table.getItems().get(index);
                    item.setActive(isNowActive);
                });
                return active ;
            });
            return cell ;
        });

        Button listActiveButton = new Button("List active");
        listActiveButton.setOnAction(e -> 
            table.getItems().stream()
                .filter(Item::isActive)
                .map(Item::getName)
                .forEach(System.out::println));

        IntStream.rangeClosed(1, 100)
            .mapToObj(i -> new Item("Item "+i, i, false))
            .forEach(table.getItems()::add);

        BorderPane root = new BorderPane(table, null, null, listActiveButton, null) ;
        BorderPane.setAlignment(listActiveButton, Pos.CENTER);
        BorderPane.setMargin(listActiveButton, new Insets(10));

        Scene scene = new Scene(root, 800, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private <S,T> TableColumn<S,T> createColumn(String title, String propertyName) {
        TableColumn<S,T> col = new TableColumn<>(title);
        col.setCellValueFactory(new PropertyValueFactory<>(propertyName));
        return col;
    }

    public static class Item implements Serializable {

        private String name ;
        private int value ;
        private boolean active ;

        public Item(String name, int value, boolean active) {
            this.name = name ;
            this.value = value ;
            this.active = active ;
        }

        public String getName() {
            return name;
        }

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

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public boolean isActive() {
            return active;
        }

        public void setActive(boolean active) {
            this.active = active;
        }

    }

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