【问题标题】:ChangeListener stops being trigger even though changes continue to happen即使更改继续发生,ChangeListener 也会停止触发
【发布时间】:2014-04-07 18:06:33
【问题描述】:

我遇到了一个看起来确实很奇怪的情况。

下面是一个简单的 javaFX 应用程序,它在 ScollPane 中包含一个 TextFlow 容器。为了在将文本添加到 TextFlow 容器时将 ScrollBar 发送到 ScrollPane 的底部,对 TextFlow 容器的 heightProperty 进行了 DoubleProperty 绑定(在最近添加 Text 后 TextFlow 容器呈现后更改),使用添加了一个 ChangeListener 以将 ScollPane 的 vValue 移动到 vMax(底部)。

添加按钮只是测试将文本添加到 TextFlow 容器。

现在奇怪的部分 - ChangeListener 完美地适用于前 9 到 11 个文本添加,之后它似乎没有通过绑定触发 heightProperty 更改(好吧,至少 ChangeListener 不再执行)。在 9 次、10 次还是 11 次之后发生这种情况似乎会随着点击 Add 按钮的速度而有所不同。

现在更奇怪的部分 - 当应用程序在调试中运行时,ChangeListener 可以完美运行(不需要断点)!

我在 Windows 8.1 笔记本电脑上使用带有 JDK1.8.0 的 Netbeans 8,所有 x64。

我希望有人可以复制这一点,并告诉我为什么在 x Text 添加后 ChangeListener 没有执行(以及为什么 它确实在调试模式下工作)。

import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;

public class TextFlowTest extends Application {

    final GridPane grid = new GridPane();
    final Button addbtn = new Button();
    final Button scrollbtn = new Button();
    final TextFlow textflow = new TextFlow();
    final ScrollPane textflowSP = new ScrollPane();
    int counter = 0;

    @Override
    public void start(Stage primaryStage) {

    // Attach a listener to the TextFlow component height property which
    // is set after component has been rendered so we can set scroll bar to
    // bottom.

        DoubleProperty hProperty = new SimpleDoubleProperty();
        hProperty.bind(textflow.heightProperty());
        hProperty.addListener(new ChangeListener() {
            @Override
            public void changed(ObservableValue ov, Object t, Object t1) {
                System.out.println("In listener " + ++counter + " with height of "
                    + Double.toString((Double)t1));
                textflowSP.setVvalue(textflowSP.getVmax());
            }
        }) ;

    // Create Scroll Pane for TextFlow component
        textflowSP.setPrefHeight(200);
        textflowSP.setPrefWidth(200);
        textflowSP.setFitToHeight(true);
        textflowSP.setFitToWidth(true);
        textflowSP.setContent(textflow);
        grid.add(textflowSP,0,1);

    // Button to add Text to TextFlow component
        addbtn.setText("Add Text");
        addbtn.setOnAction((ActionEvent e) -> {
            textflow.getChildren().add(new Text("Line 1\nLine 2\nLine 3\n-\n"));
            // Print shows that height property has not changed after adding Text
            // but before rendering.
            System.out.println(Double.toString(textflow.heightProperty().get()));
        });

    // Button to scoll to bottom of Scroll Pane
        scrollbtn.setText("Scroll to Bottom");
        scrollbtn.setOnAction((ActionEvent e) -> {
            textflowSP.setVvalue(textflowSP.getVmax());
        });

        VBox btnVB = new VBox();
        btnVB.getChildren().addAll(addbtn,scrollbtn);
        grid.add(btnVB,1,1);

        Scene scene = new Scene(grid, 330, 210);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

}

提前感谢您提供的任何帮助。

【问题讨论】:

    标签: javafx changelistener


    【解决方案1】:

    查看来自bind 函数的文档:

    请注意,JavaFX 的所有绑定调用都是通过弱侦听器实现的。这意味着绑定的属性可以被垃圾收集并停止更新。

    hProperty 和 textflow.heightProperty 之间的绑定是通过弱侦听器完成的。所以没有什么可以阻止 hProperty 被垃圾收集。

    详细情况:

    1. 您的程序从 main 函数开始
    2. 您的程序通过调用启动来启动 JavaFX
    3. 在启动函数中将调用启动方法
    4. hProperty 将填充一个对 SimpleDoubleProperty 的新引用
    5. 启动方法返回,JavaFX 进入主循环。当函数返回时,该函数的所有局部变量都将被释放。目前,您的 hProperty 不存在硬参考。在 heightProperty 的绑定列表中只剩下一个弱引用。
    6. hProperty 变成垃圾回收
    7. 为 textflow.heightProperty 上的绑定注册的侦听器识别垃圾收集操作并将它们自己从 textflow.heightProperty 中删除

    这就是您的绑定在下一个 GC 循环之前有效的原因。

    为了防止这种情况,您必须在本地函数范围之外的某个地方存储对 hProperty 的引用,例如在 TextFlowTest 的范围内。或者直接在 height-property 上注册你的听众:

    textflow.heightProperty().addListener((obs, ov, nv) -> { ... });
    

    【讨论】:

      【解决方案2】:

      我遇到了类似的问题。我通过在任何方法之外声明要绑定的属性(在本例中为 DoubleProperty hProperty = new SimpleDoubleProperty();)来解决它,因此它不仅仅是一个局部变量。

      【讨论】:

        猜你喜欢
        • 2017-02-26
        • 2020-09-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-29
        • 2022-12-07
        • 2022-01-11
        相关资源
        最近更新 更多