基于TableView中选定项目的JavaFX布尔绑定
我试图启用JavaFX Button
,具体取决于TableView
中选定行中属性值的聚合。以下是一个示例应用程序,演示了此问题:基于TableView中选定项目的JavaFX布尔绑定
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();
}
}
测试,启动上述应用程序,并选择由文本“翻转行N”,其中N是在所指示的列[0,2]。当所选行的值更改为真时...
观察到的行为button
仍然被禁用。
希望的行为button
变为启用。
有谁知道如何创建一个BooleanBinding
展现出所需的行为?
如果所选行的任何一个propProperty
发生更改,则您的绑定需要失效。目前绑定仅观察选定的项目列表,当列表内容改变时(即,项目变为被选择或未被选择),该绑定将触发事件,但当属于该列表中的项目的属性改变值时,绑定不会被触发。
要做到这一点,创建一个列表与提取:当项目被添加或删除,或
final ObservableList<Row> selectedRows =
FXCollections.observableArrayList(r -> new Observable[]{r.propProperty()});
这份名单将触发事件时,任何项目的列表中的变化propProperty()
。 (如果需要观察多个值,可以将它们包含在Observable
的数组中)。
当然,您仍然需要此列表来包含表中选定的项目。您可以通过绑定列表中选择模型的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(...)'用提取器创建了一个新的'ObservableList', Bindings.bindContent(...)'将新列表的内容绑定到'TableView'中的选定行。 –
我遇到了这种方法的问题,我想在其他人遇到同样问题时记录这个问题。当我在表中选择一行或多行时,在主UI线程上调用'table.getItems()。clear()',我得到一个'IndexOutOfBoundsException'。为了解决这个问题,我改变了调用clear清单来确定列表> items = table.getItems(); while(!items.isEmpty())items.remove(items.size() - 1);',然后改变for循环为'for(final Row selectedRow:selectionModel.getSelectedItems())',而不是迭代在'selectedRows'列表中。 –
仅供参考,您需要执行JavaFX代码在JavaFX线程上。 'final BooleanProperty prop = rows.get(i).propProperty(); prop.set(!prop.get());'应该用'Platform.runLater()'包装。 –