【问题标题】:JavaFX Boolean Binding Based on Selected Items in a TableView基于 TableView 中选定项的 JavaFX 布尔绑定
【发布时间】:2016-09-21 19:27:19
【问题描述】:

我正在尝试启用 JavaFX Button,具体取决于 TableView 中选定行中的属性值的聚合。以下是演示该问题的示例应用程序:

package test;

import java.util.Random;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.MultipleSelectionModel;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

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

    private static class Row {
        private final BooleanProperty myProp;
        public Row(final boolean value) {
            myProp = new SimpleBooleanProperty(value);
        }
        public BooleanProperty propProperty() { return myProp; }
    }

    @Override
    public void start(final Stage window) throws Exception {
        // Create a VBox to hold the table and button
        final VBox root = new VBox();
        root.setMinSize(200, 200);

        // Create the table, and enable multi-select
        final TableView<Row> table = new TableView<>();
        final MultipleSelectionModel<Row> selectionModel = table.getSelectionModel();
        selectionModel.setSelectionMode(SelectionMode.MULTIPLE);
        root.getChildren().add(table);

        // Create a column based on the value of Row.propProperty()
        final TableColumn<Row, Boolean> column = new TableColumn<>("Value");
        column.setCellValueFactory(p -> p.getValue().propProperty());
        table.getColumns().add(column);

        // Add a button below the table
        final Button button = new Button("Button");
        root.getChildren().add(button);

        // Populate the table with true/false values
        final ObservableList<Row> rows = table.getItems();
        rows.addAll(new Row(false), new Row(false), new Row(false));

        // Start a thread to randomly modify the row values
        final Random rng = new Random();
        final Thread thread = new Thread(() -> {
            // Flip the value in a randomly selected row every 10 seconds
            try {
                do {
                    final int i = rng.nextInt(rows.size());
                    System.out.println("Flipping row " + i);
                    Thread.sleep(10000);
                    final BooleanProperty prop = rows.get(i).propProperty();
                    prop.set(!prop.get());
                } while (true);
            } catch (final InterruptedException e) {
                System.out.println("Exiting Thread");
            }
        }, "Row Flipper Thread");
        thread.setDaemon(true);
        thread.start();

        // Bind the button's disable property such that the button
        //     is only enabled if one of the selected rows is true
        final ObservableList<Row> selectedRows = selectionModel.getSelectedItems();
        button.disableProperty().bind(Bindings.createBooleanBinding(() -> {
            for (int i = 0; i < selectedRows.size(); ++i) {
                if (selectedRows.get(i).propProperty().get()) {
                    return false;
                }
            }
            return true;
        }, selectedRows));

        // Show the JavaFX window
        final Scene scene = new Scene(root);
        window.setScene(scene);
        window.show();
    }
}

为了测试,启动上面的应用程序,并选择文本“Flipping row N”所指示的行,其中N在[0, 2]中。当所选行的值更改为true ...

观察到的行为 button 保持禁用状态。
所需行为 button 变为启用状态。

有谁知道如何创建一个展示所需行为的BooleanBinding

【问题讨论】:

  • 仅供参考,您需要在 JavaFX 线程上执行 JavaFX 代码。 final BooleanProperty prop = rows.get(i).propProperty(); prop.set(!prop.get()); 应该用 Platform.runLater() 包裹起来。

标签: java javafx java-8 javafx-8


【解决方案1】:

如果所选行的任何propPropertys 发生更改,则您的绑定需要失效。目前绑定只观察选中的项目列表,当列表内容改变(即项目被选中或取消选中)时会触发事件,但当属于该列表中的项目的属性值发生变化时不会触发。

为此,请使用提取器创建一个列表:

final ObservableList<Row> selectedRows = 
    FXCollections.observableArrayList(r -> new Observable[]{r.propProperty()});

当添加或删除项目时,或者当列表中任何项目的propProperty() 更改时,此列表将触发事件。 (如果需要观察多个值,可以将它们包含在Observables 的数组中。)

当然,您仍然需要此列表来包含表格中的选定项目。您可以通过将列表的内容绑定到选择模型的selectedItems 来确保这一点:

Bindings.bindContent(selectedRows, selectionModel.getSelectedItems());

这是你的 MCVE 的一个版本,使用这个:

import java.util.Random;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.MultipleSelectionModel;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

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

    private static class Row {
        private final BooleanProperty myProp;
        public Row(final boolean value) {
            myProp = new SimpleBooleanProperty(value);
        }
        public BooleanProperty propProperty() { return myProp; }
    }

    @Override
    public void start(final Stage window) throws Exception {
        // Create a VBox to hold the table and button
        final VBox root = new VBox();
        root.setMinSize(200, 200);

        // Create the table, and enable multi-select
        final TableView<Row> table = new TableView<>();
        final MultipleSelectionModel<Row> selectionModel = table.getSelectionModel();
        selectionModel.setSelectionMode(SelectionMode.MULTIPLE);
        root.getChildren().add(table);

        // Create a column based on the value of Row.propProperty()
        final TableColumn<Row, Boolean> column = new TableColumn<>("Value");
        column.setCellValueFactory(p -> p.getValue().propProperty());
        table.getColumns().add(column);

        // Add a button below the table
        final Button button = new Button("Button");
        root.getChildren().add(button);

        // Populate the table with true/false values
        final ObservableList<Row> rows = table.getItems();
        rows.addAll(new Row(false), new Row(false), new Row(false));

        // Start a thread to randomly modify the row values
        final Random rng = new Random();
        final Thread thread = new Thread(() -> {
            // Flip the value in a randomly selected row every 10 seconds
            try {
                do {
                    final int i = rng.nextInt(rows.size());
                    System.out.println("Flipping row " + i);
                    Thread.sleep(10000);
                    final BooleanProperty prop = rows.get(i).propProperty();
                    Platform.runLater(() -> prop.set(!prop.get()));
                } while (true);
            } catch (final InterruptedException e) {
                System.out.println("Exiting Thread");
            }
        }, "Row Flipper Thread");
        thread.setDaemon(true);
        thread.start();


        // Bind the button's disable property such that the button
        //     is only enabled if one of the selected rows is true
        final ObservableList<Row> selectedRows = 
                FXCollections.observableArrayList(r -> new Observable[]{r.propProperty()});
        Bindings.bindContent(selectedRows, selectionModel.getSelectedItems());
        button.disableProperty().bind(Bindings.createBooleanBinding(() -> {
            for (int i = 0; i < selectedRows.size(); ++i) {
                if (selectedRows.get(i).propProperty().get()) {
                    return false;
                }
            }
            return true;
        }, selectedRows));

        // Show the JavaFX window
        final Scene scene = new Scene(root);
        window.setScene(scene);
        window.show();
    }
}

【讨论】:

  • 请注意,更改是在设置属性值的线程中添加Platform.runLater(...)FXCollections.observableArrayList(...) 使用提取器创建新的ObservableListBindings.bindContent(...) 绑定内容TableView 中选定行的新列表。
  • 我遇到了这种方法的问题,我想记录下来以防其他人遇到同样的问题。当我在表中选择一行或多行,然后在主 UI 线程上调用 table.getItems().clear() 时,我得到一个 IndexOutOfBoundsException。为了解决这个问题,我将调用 clear 更改为 final List&lt;?&gt; items = table.getItems(); while (!items.isEmpty()) items.remove(items.size() - 1);,然后将 for 循环更改为 for (final Row selectedRow : selectionModel.getSelectedItems()),而不是遍历 selectedRows 列表。
猜你喜欢
  • 1970-01-01
  • 2013-03-31
  • 2015-06-19
  • 2019-02-13
  • 2013-06-27
  • 2018-01-17
  • 2012-08-28
  • 2012-05-28
  • 2013-09-08
相关资源
最近更新 更多