Java绑定:BindBidirectional不起作用(Java Bindings: BindBidirectional not work)

编程入门 行业动态 更新时间:2024-10-25 04:22:00
Java绑定:BindBidirectional不起作用(Java Bindings: BindBidirectional not work)

更新

我的问题是双向绑定( bindBidirectional )不起作用。

我发现了一种使用以下代码重现行为的方法:

这是FXML:

<VBox spacing="5.0" xmlns="http://javafx.com/javafx/null" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.test.MyTestPaneView"> <children> <Label text="This is a Test Pane" /> <ScrollPane fitToWidth="true"> <content> <FlowPane fx:id="pnl" /> </content> </ScrollPane> </children> </VBox>

这是控制器类:

public class MyTestPaneView implements Initializable{ private static final int MAX = 1000; private static final Random RANDOM = new Random(); @FXML private FlowPane pnl; private final BooleanProperty[] modelArray = new BooleanProperty[MAX]; private final Node[] viewArray = new Node[MAX]; private final ToggleGroup rbGrp = new ToggleGroup(), btnGrp = new ToggleGroup(); @Override public void initialize(URL location, ResourceBundle resources) { for (int i=0; i<MAX; i++) { final int index = i; modelArray[i] = new SimpleBooleanProperty(); modelArray[i].addListener(observable -> System.out.println("Model Update: " + index)); switch (Math.abs(RANDOM.nextInt() % 3)) { case 0: final CheckBox ckb = new CheckBox("CKB " + i); ckb.selectedProperty().addListener(observable -> System.out.println("View Update: " + index)); ckb.selectedProperty().bindBidirectional(modelArray[i]); viewArray[i] = ckb; break; case 1: final RadioButton rb = new RadioButton("RB " + i); rb.setToggleGroup(rbGrp); rb.selectedProperty().addListener(observable -> System.out.println("View Update: " + index)); rb.selectedProperty().bindBidirectional(modelArray[i]); viewArray[i] = rb; break; case 2: final ToggleButton btn = new ToggleButton("BTN " + i); btn.setToggleGroup(btnGrp); btn.selectedProperty().addListener(observable -> System.out.println("View Update: " + index)); btn.selectedProperty().bindBidirectional(modelArray[i]); viewArray[i] = btn; break; } pnl.getChildren().add(viewArray[i]); } } }

这是基本窗格:

public class MyTestPane extends BorderPane { public MyTestPane() { try { FXMLLoader loader = new FXMLLoader(); loader.setLocation(Thread.currentThread().getContextClassLoader().getResource("org/test/MyTestPaneView.fxml")); loader.setControllerFactory(cl -> new MyTestPaneView()); final Pane pane = loader.load(); setCenter(pane); } catch (IOException e) { throw new RuntimeException(e); } } }

要运行的代码:

public class Runner extends Application { public static void main(String[] args) { launch(args); } public void start(Stage stage) { stage.setScene(new Scene(new MyTestPane(), 1280, 1024)); stage.show(); } }

预期的行为是每次点击任何组件都会产生2条输出线:一条用于UI更改,另一条用于背景模型更改:

View Update: 127 Model Update: 127 View Update: 405 Model Update: 405

在某些情况下,它显示如下:

View Update: 5 Model Update: 5 View Update: 233 View Update: 509 View Update: 12 Model Update: 12

如果将其复制到项目中并运行它,您会注意到按钮和复选框的第一部分正常工作而最后一部分没有。 有多少节点无法正常工作因执行而异。

此代码的核心是无法正常工作的双向绑定。

这是JVM中的错误吗? 我使用Java 1.8 U60 / U66(x64)和Windows 7 x64运行它。

UPDATED

My problem is that the bi-directional binding (bindBidirectional) not works.

I found a way to reproduce the behavior with the following code:

This is the FXML:

<VBox spacing="5.0" xmlns="http://javafx.com/javafx/null" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.test.MyTestPaneView"> <children> <Label text="This is a Test Pane" /> <ScrollPane fitToWidth="true"> <content> <FlowPane fx:id="pnl" /> </content> </ScrollPane> </children> </VBox>

This is the controller class:

public class MyTestPaneView implements Initializable{ private static final int MAX = 1000; private static final Random RANDOM = new Random(); @FXML private FlowPane pnl; private final BooleanProperty[] modelArray = new BooleanProperty[MAX]; private final Node[] viewArray = new Node[MAX]; private final ToggleGroup rbGrp = new ToggleGroup(), btnGrp = new ToggleGroup(); @Override public void initialize(URL location, ResourceBundle resources) { for (int i=0; i<MAX; i++) { final int index = i; modelArray[i] = new SimpleBooleanProperty(); modelArray[i].addListener(observable -> System.out.println("Model Update: " + index)); switch (Math.abs(RANDOM.nextInt() % 3)) { case 0: final CheckBox ckb = new CheckBox("CKB " + i); ckb.selectedProperty().addListener(observable -> System.out.println("View Update: " + index)); ckb.selectedProperty().bindBidirectional(modelArray[i]); viewArray[i] = ckb; break; case 1: final RadioButton rb = new RadioButton("RB " + i); rb.setToggleGroup(rbGrp); rb.selectedProperty().addListener(observable -> System.out.println("View Update: " + index)); rb.selectedProperty().bindBidirectional(modelArray[i]); viewArray[i] = rb; break; case 2: final ToggleButton btn = new ToggleButton("BTN " + i); btn.setToggleGroup(btnGrp); btn.selectedProperty().addListener(observable -> System.out.println("View Update: " + index)); btn.selectedProperty().bindBidirectional(modelArray[i]); viewArray[i] = btn; break; } pnl.getChildren().add(viewArray[i]); } } }

And this is the basic pane:

public class MyTestPane extends BorderPane { public MyTestPane() { try { FXMLLoader loader = new FXMLLoader(); loader.setLocation(Thread.currentThread().getContextClassLoader().getResource("org/test/MyTestPaneView.fxml")); loader.setControllerFactory(cl -> new MyTestPaneView()); final Pane pane = loader.load(); setCenter(pane); } catch (IOException e) { throw new RuntimeException(e); } } }

Code to run:

public class Runner extends Application { public static void main(String[] args) { launch(args); } public void start(Stage stage) { stage.setScene(new Scene(new MyTestPane(), 1280, 1024)); stage.show(); } }

Expected behavior is that each click on any component produces 2 output lines: one for the UI change and one for the background model change:

View Update: 127 Model Update: 127 View Update: 405 Model Update: 405

In some cases it shows so:

View Update: 5 Model Update: 5 View Update: 233 View Update: 509 View Update: 12 Model Update: 12

If you copy it into a project and run it you will notice that the first part of buttons and checkboxes work correctly and the last part not. How many nodes not work correctly differs from execution to execution.

The core of this code is the bidirectional binding that not work correctly.

Is it a bug in JVM? I run it with Java 1.8 U60 / U66 (x64) and Windows 7 x64.

最满意答案

这基本上是JavaFX Beans的重复绑定突然停止工作您的绑定正在收集垃圾,因为您没有保留对使用它们的任何内容的引用。

在大多数情况下,在“真实”代码(即非示例代码)中,您保留对模型的引用,因为它将在UI中的不同组件之间共享,因此这不是问题。

你可以通过强制垃圾收集来重现问题:

import java.io.IOException; import javafx.fxml.FXMLLoader; import javafx.scene.control.Button; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; public class MyTestPane extends BorderPane { public MyTestPane() { try { FXMLLoader loader = new FXMLLoader(); // loader.setLocation(Thread.currentThread().getContextClassLoader().getResource("MyTestPaneView.fxml")); loader.setLocation(getClass().getResource("MyTestPaneView.fxml")); loader.setControllerFactory(cl -> new MyTestPaneView()); final Pane pane = loader.load(); setCenter(pane); Button gc = new Button("GC"); gc.setOnAction(e -> System.gc()); setBottom(gc); } catch (IOException e) { throw new RuntimeException(e); } } }

在此版本中,按“GC”按钮后,所有型号更新都将停止。

您可以通过在自定义控件类中保留对控制器的引用来解决此问题:

import java.io.IOException; import javafx.fxml.FXMLLoader; import javafx.scene.control.Button; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; public class MyTestPane extends BorderPane { private MyTestPaneView controller ; public MyTestPane() { try { FXMLLoader loader = new FXMLLoader(); // loader.setLocation(Thread.currentThread().getContextClassLoader().getResource("MyTestPaneView.fxml")); loader.setLocation(getClass().getResource("MyTestPaneView.fxml")); loader.setControllerFactory(cl -> new MyTestPaneView()); final Pane pane = loader.load(); controller = loader.getController(); setCenter(pane); Button gc = new Button("GC"); gc.setOnAction(e -> System.gc()); setBottom(gc); } catch (IOException e) { throw new RuntimeException(e); } } }

显然,强制保留这些引用的确切细节可能会因实际代码而异,但这应该足以解决问题。

您可能也对http://tomasmikula.github.io/blog/2015/02/10/the-trouble-with-weak-listeners.html感兴趣

This is basically a duplicate of JavaFX Beans Binding suddenly stops working Your bindings are getting garbage collected because you don't retain a reference to anything that uses them.

In most cases in "real" code (i.e. not example code) you retain references to your model, as it will be shared among different components in the UI, so this is not an issue.

You can for the problem to be reproduced by forcing garbage collection:

import java.io.IOException; import javafx.fxml.FXMLLoader; import javafx.scene.control.Button; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; public class MyTestPane extends BorderPane { public MyTestPane() { try { FXMLLoader loader = new FXMLLoader(); // loader.setLocation(Thread.currentThread().getContextClassLoader().getResource("MyTestPaneView.fxml")); loader.setLocation(getClass().getResource("MyTestPaneView.fxml")); loader.setControllerFactory(cl -> new MyTestPaneView()); final Pane pane = loader.load(); setCenter(pane); Button gc = new Button("GC"); gc.setOnAction(e -> System.gc()); setBottom(gc); } catch (IOException e) { throw new RuntimeException(e); } } }

In this version, once you press the "GC" button, all model updates will stop.

You can fix the problem here by retaining a reference to the controller in your custom control class:

import java.io.IOException; import javafx.fxml.FXMLLoader; import javafx.scene.control.Button; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; public class MyTestPane extends BorderPane { private MyTestPaneView controller ; public MyTestPane() { try { FXMLLoader loader = new FXMLLoader(); // loader.setLocation(Thread.currentThread().getContextClassLoader().getResource("MyTestPaneView.fxml")); loader.setLocation(getClass().getResource("MyTestPaneView.fxml")); loader.setControllerFactory(cl -> new MyTestPaneView()); final Pane pane = loader.load(); controller = loader.getController(); setCenter(pane); Button gc = new Button("GC"); gc.setOnAction(e -> System.gc()); setBottom(gc); } catch (IOException e) { throw new RuntimeException(e); } } }

Obviously the exact details of how you force those references to be retained may vary in your actual code, but this should give you enough to fix the problem.

You might also be interested in http://tomasmikula.github.io/blog/2015/02/10/the-trouble-with-weak-listeners.html

更多推荐

本文发布于:2023-07-16 07:29:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1125438.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:绑定   不起作用   Java   BindBidirectional   work

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!