gpt4 book ai didi

java - InvalidationListener 仅在带有断点的 Debug模式下执行

转载 作者:行者123 更新时间:2023-11-29 07:32:19 25 4
gpt4 key购买 nike

我的 InvalidationListener 有问题。它被设置为 SimpleStringProperty 上的监听器。但它只在第一次更改 SimpleStringProperty 时调用。我进入了 Debug模式并在调用 SimpleStringProperty::set 的行上创建了一个断点,它开始工作,直到我再次删除断点。

我制作了一个简短的可执行示例程序,它模拟了带有计时器的 SimpleStringProperty 的修改。您可以在没有断点的情况下运行程序一次,在这一行有一个断点:property.set(value);

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Duration;


public class Main extends Application {

private SimpleStringProperty property;
private int counter;

@Override
public void start(Stage stage) {
// open a window to avoid immediately termination
stage.setWidth(800);
stage.setHeight(600);
BorderPane pane = new BorderPane();
stage.setScene(new Scene(pane));
stage.show();

// create a SimpleObjectProperty
property = new SimpleStringProperty();
property.addListener(observable ->
System.out.println("New value is: " + counter)
);
counter = 0;

// create timer to change 'property' every second
Timeline timeline = new Timeline();
KeyFrame keyFrame = new KeyFrame(Duration.seconds(2), event ->{
String value = "" + ++counter;
System.out.println("Set property to: " + value);
property.set(value);
});
timeline.getKeyFrames().add(keyFrame);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.playFromStart();
}

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

我机器上的输出(Linux Mint 16.04 64 位,Oracle-Java 1.8.0_111):

Set property to: 1
New value is: 1
Set property to: 2
Set property to: 3
Set property to: 4
...

请给我解释一下:

  1. 为什么不在每次更改时调用监听器?
  2. 当我设置断点时,为什么会调用监听器?
  3. 我应该怎么做才能让它在没有断点的情况下工作?

最佳答案

一个可观察值有两种不同的状态,它们的变化可以触发监听器。有它的,还有它当前是否有效的状态。

通常,可观察值的值可能是经过计算的值,而不是简单地存储在字段中。一旦该值被“实现”(我的术语),如果它被计算,则通过计算,或者如果它只是存储在一个字段中,则通过被检索,那么可观察值就处于“有效”状态。如果值发生变化(或可能已经发生变化),则可观察值变为“无效”,表明可能需要重新计算或再次查找。

仅当可观察值从有效状态转换为无效状态时,才会触发失效监听器。所以在你的代码中,你第一次调用

property.set(value);

属性转换为无效状态(因为最近检索到的值(如果有的话)不是其当前值)。

由于您从未调用过 property.get()(或 property.getValue()),因此该属性永远不会得到验证。因此,下次您调用 property.set(value) 时,该属性不会转换到无效状态(它已经处于该状态),因此监听器没有被解雇。

如果您将监听器代码替换为

property.addListener(observable ->
System.out.println("New value is: " + property.get())
);

此监听器将使该属性再次生效,因此每次都会触发监听器。

真正的问题是您在这里使用了错误类型的监听器。如果您想每次值更改时执行操作,请使用ChangeListener,而不是InvalidationListener:

property.addListener((observable, oldValue, newValue) -> 
System.out.println("New value is: " + newValue)
);

在带有断点的 Debug模式下运行会导致每次调用失效监听器的观察结果很有趣。我有点猜测,但我怀疑正在发生的事情是,当您到达断点时,调试器会显示变量的当前值。这不可避免地涉及对属性调用 getValue()(可能作为其 toString() 实现的一部分),因此属性得到验证。

您不太可能经常显式使用 InvalidationListener。它们的主要用途是绑定(bind)。考虑以下示例:

DoubleProperty x = new SimpleDoubleProperty(3);
DoubleProperty y = new SimpleDoubleProperty(4);

DoubleBinding hyp = new DoubleBinding() {
{
bind(x);
bind(y);
}

@Override
protected double computeValue() {
System.out.println("Computing distance");
return Math.sqrt(x.get()*x.get() + y.get()*y.get());
}
};

Label hypLabel = new Label();
hypLabel.textProperty().bind(hyp.asString("Hypotenuse: %f"));

在绑定(bind)实现中调用bind(x)意味着:当x失效时,认为本次绑定(bind)失效。 y 也类似。当然,bind 的实现在底层使用了 InvalidationListener

这里的要点是 hyp 的值的计算非常昂贵。如果您要对 xy 进行多项更改,则 hyp 将变得无效,并且需要在您再次需要时重新计算 。由于标签的文本属性绑定(bind)到 hyp,这也意味着标签的文本无效。但是,只有在渲染脉冲中重新绘制标签时,您才真正需要新值;为 xy 的每次更改计算值就太过分了。

关于java - InvalidationListener 仅在带有断点的 Debug模式下执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40477770/

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