即使更改继续发生,ChangeListener也会停止触发

时间:2014-04-07 18:06:33

标签: javafx changelistener

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

下面是一个简单的javaFX应用程序,其中包含一个包含在ScollPane中的TextFlow容器。为了将ScrollBar发送到ScrollPane的底部,因为Text被添加到TextFlow容器中,DoubleProperty绑定到了TextFlow容器的heightProperty(在TextFlow容器在最新添加Text之后呈现后更改),添加了ChangeListener以将ScollPane的vValue移动到vMax(底部)。

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

现在奇怪的部分 - ChangeListener完美地适用于前9到11个文本添加,之后看起来不会通过绑定触发heightProperty更改(好吧,至少ChangeListener不再执行)。是否在9,10或11次之后发生似乎随着点击添加按钮的速度而变化。

现在更奇怪的部分 - 当应用程序在调试中运行时,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);
    }

}

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

2 个答案:

答案 0 :(得分:1)

查看bind函数中的文档:

  

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

hProperty和textflow.heightProperty之间的绑定是通过弱侦听器完成的。因此,没有什么能阻止hProperty收集垃圾。

细节会发生什么:

  1. 您的程序以主要功能
  2. 开头
  3. 您的程序通过调用launch
  4. 启动JavaFX
  5. 在启动函数中,将调用start方法
  6. hProperty将填充一个新的SimpleDoubleProperty参考
  7. start-method返回,JavaFX进入Main-loop。当函数返回时,将释放该函数的所有局部变量。此时,您的hProperty不存在硬参考。只有一个弱引用留在heightProperty的bindings-list中。
  8. hProperty变成垃圾收集
  9. 在textflow.heightProperty上注册绑定的侦听器识别垃圾收集操作并从textflow.heightProperty中删除它们自己
  10. 这就是你的装订工作到下一个GC循环的原因。

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

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

答案 1 :(得分:0)

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