gpt4 book ai didi

JavaFx 绑定(bind) : is there any way to bind value to observable List?

转载 作者:行者123 更新时间:2023-12-02 09:13:18 27 4
gpt4 key购买 nike

我有一个 TableView ,其内容是一个包含数字的 ObservableList,并且我有一个文本字段,应显示表中这些值的总和。有什么方法可以将此文本字段绑定(bind)到数字属性的总和吗?

注意:用户可以编辑此列表中的值,可以添加更多元素,也可以删除某些元素。如何使用 JavaFx 绑定(bind)正确绑定(bind)这些数字的总和,而不是通过旧的时尚方式迭代列表并手动对数字求和,然后再次重复每次更改?

最佳答案

ObservableList 将触发 update events如果(且仅当)您使用 extractor 创建列表。提取器是一个函数,它将列表的每个元素映射到 Observables 的数组。 ;如果这些 Observable 中的任何一个更改了它们的值,则该列表会触发适当的更新事件并变得无效。

所以这里的两个步骤是:

  1. 使用提取器创建列表
  2. 创建一个绑定(bind),以便在列表失效时计算总数。

因此,如果您的表有一个模型类,例如:

public class Item {

private final IntegerProperty value = new SimpleIntegerProperty();

public IntegerProperty valueProperty() {
return value ;
}

public final int getValue() {
return valueProperty().get();
}

public final void setValue(int value) {
valueProperty().set(value);
}

// other properties, etc...
}

然后您使用以下内容创建表格:

TableView<Item> table = new TableView<>();
table.setItems(FXCollections.observableArrayList(item ->
new Observable[] { item.valueProperty() }));

现在您可以使用以下命令创建绑定(bind)

IntegerBinding total = Bindings.createIntegerBinding(() ->
table.getItems().stream().collect(Collectors.summingInt(Item::getValue)),
table.getItems());

实现说明:上面 createIntegerBinding 的两个参数是计算 int 值以及要观察的任何值的函数。如果任何观察到的值(这里只有一个,table.getItems())无效,则重新计算该值。请记住,我们创建了 table.getItems(),因此如果任何项目的 valueProperty() 发生更改,它就会失效。第一个参数的函数使用 lambda 表达式和 Java 8 Streams API,它大致相当于

() -> {
int totalValue = 0 ;
for (Item item : table.getItems()) {
totalValue = totalValue + item.getValue();
}
return totalValue ;
}

最后,如果你想要一个标签来显示总数,你可以这样做

Label totalLabel = new Label();
totalLabel.textProperty().bind(Bindings.format("Total: %d", total));

这是一个 SSCCE:

import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.IntegerBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import javafx.util.converter.DefaultStringConverter;
import javafx.util.converter.NumberStringConverter;

public class TotallingTableView extends Application {

@Override
public void start(Stage primaryStage) {

TableView<Item> table = new TableView<>();
table.setEditable(true);

table.getColumns().add(
column("Item", Item::nameProperty, new DefaultStringConverter()));

table.getColumns().add(
column("Value", Item::valueProperty, new NumberStringConverter()));

table.setItems(FXCollections.observableArrayList(
item -> new Observable[] {item.valueProperty() }));

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

IntegerBinding total = Bindings.createIntegerBinding(() ->
table.getItems().stream().collect(Collectors.summingInt(Item::getValue)),
table.getItems());

Label totalLabel = new Label();
totalLabel.textProperty().bind(Bindings.format("Total: %d", total));

Button add = new Button("Add item");
add.setOnAction(e ->
table.getItems().add(new Item("New Item", table.getItems().size() + 1)));

Button remove = new Button("Remove");
remove.disableProperty().bind(
Bindings.isEmpty(table.getSelectionModel().getSelectedItems()));

remove.setOnAction(e ->
table.getItems().remove(table.getSelectionModel().getSelectedItem()));

HBox buttons = new HBox(5, add, remove);
buttons.setAlignment(Pos.CENTER);
VBox controls = new VBox(5, totalLabel, buttons);
VBox.setVgrow(totalLabel, Priority.ALWAYS);
totalLabel.setMaxWidth(Double.MAX_VALUE);
totalLabel.setAlignment(Pos.CENTER_RIGHT);

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

private <S,T> TableColumn<S,T> column(String title,
Function<S, ObservableValue<T>> property, StringConverter<T> converter) {
TableColumn<S,T> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));

col.setCellFactory(TextFieldTableCell.forTableColumn(converter));

return col ;
}

public static class Item {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty value = new SimpleIntegerProperty();

public Item(String name, int value) {
setName(name);
setValue(value);
}

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 static void main(String[] args) {
launch(args);
}
}

请注意,这不是最有效的实现,但(恕我直言)使代码保持最干净的实现。如果表中有大量项目,通过迭代并对所有项目求和来从头开始重新计算总数可能会非常昂贵。另一种方法是监听对列表的添加/删除更改。添加项目时,将其值添加到总计中,并使用 value 属性注册一个监听器,该属性会在值发生变化时更新总计。当从列表中删除项目时,从 value 属性中删除监听器并从总计中减去该值。这避免了不断地从头开始重新计算,但代码更难破译。

关于JavaFx 绑定(bind) : is there any way to bind value to observable List?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29785996/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com