[英]Why does calling get of a bound value in the binding throw a stackoverflow error
獲取綁定的標簽的值時,此代碼在標簽綁定中引發stackoverflowerror。 我希望標簽最初是“測試”,然后在第一次按下“測試按下”,然后是“按下測試”,依此類推。 但是,讀取值會引發stackoverflowerror,因為調用getText()方法會觸發綁定。 我希望只有按鈕按下事件才能觸發綁定。
注意:我已注釋掉導致錯誤的代碼,並添加了另一個按鈕以更好地顯示我的困惑。
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application{
@Override
public void start(Stage primaryStage) {
Label l = new Label("test");
Button b = new Button("press me");
l.textProperty().bind(Bindings.createStringBinding(() ->{
System.out.println("changing label text");
return "ok";
//return l.getText() + " pressed"; //Causes a stackoverflow error
},b.pressedProperty()));
Button b2 = new Button("press me 2");
b2.pressedProperty().addListener((o) -> {
l.getText(); //Why does this not triggger the binding?
});
VBox root = new VBox();
root.getChildren().addAll(l,b,b2);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Binding test");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args){
launch(args);
}
}
我的目標是在特定條件下具有不會更改文本的綁定。 Callable Lambda中的邏輯類似於:
if(condition){
return "ok";
}else{
return l.getText(); //if the condition is not met then use the current value.
}
我知道我可以在被按下的屬性上使用偵聽器,然后以這種方式設置文本標簽,因此我有一個解決方案,但我想知道為什么會發生上述情況。
僅在語義上,綁定表示規則,即標簽的文本是與" pressed"
串聯的標簽的文本。 顯然,這是說標簽的文本取決於標簽的文本,因此它是遞歸的。
無論如何,我認為這不是您要強加的規則。 我認為您希望規則為:如果未按下按鈕,則標簽文本為"test"
如果未按下按鈕,則規則為"test pressed"
。(現在,如果按鈕的pressed
屬性更改,則告訴綁定重新計算,但該值實際上並不取決於該屬性。)
從技術上講,正在發生的事情大致如下:
public class Label {
private final StringProperty textProperty = new SimpleStringProperty() ;
public String getText() {
return textProperty().get();
}
// ...
}
和
public class SimpleStringProperty {
private StringBinding binding ;
private boolean bound ;
private String value ;
// ...
public String get() {
if (bound) {
value = binding.get();
}
return value ;
}
public void bind(StringBinding binding) {
bound = true ;
this.binding = binding ;
value = binding.get();
}
}
最后,字符串綁定具有以下幾方面的邏輯:
public abstract class StringBinding {
private boolean valid = false;
private String value ;
protected void bind(ObservableStringValue dependency) {
dependency.addListener(o -> invalidate());
}
private void invalidate() {
valid = false ;
// notify invalidation listeners...
}
public String get() {
if (!valid) {
value = computeValue();
valid = true ;
}
return value ;
}
public abstract String computeValue();
}
在您的示例中, computeValue()
的實現將調用標簽的getText()
方法。
因此,當您創建綁定時,將從綁定的值中設置標簽的text屬性的值。 綁定無效(因為尚未計算),因此通過您提供的方法進行計算。 該方法調用label.getText()
,該方法從屬性獲取值。 因為該屬性已綁定,所以它將檢查仍然無效的綁定(因為尚未完成其值的計算),因此它將計算其值,從而調用label.getText()
...
因此,您可能想要以下內容:
label.textProperty().bind(Bindings.createStringBinding(() -> {
if (b.isPressed()) {
return "test pressed";
} else {
return "test";
}
}, b.pressedProperty());
如果要更改基礎字符串,則需要為其創建一個新屬性:
StringProperty text = new SimpleStringProperty("test");
label.textProperty().bind(Bindings.createStringBinding(() -> {
if (b.isPressed)() {
return text.get() + " pressed" ;
} else {
return text.get();
}
}, text, b.pressedProperty());
或者,等效地
label.textProperty().bind(text.concat(
Bindings.when(b.pressedProperty())
.then(" pressed")
.otherwise("")));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.