I'm developing a desktop login GUI currently, and I trying to change a label's visibility while a boolean value isLoggedIn is changed. I simply reproduce my problem with intelliJ's javaFX sample project:
view: hello-view.fxml
A simple GUI with a label and a button.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/18" fx:controller="com.example.bind_test.HelloController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
<Label fx:id="welcomeText" text="SAMPLE TEXT" />
<Button onAction="#onHelloButtonClick" text="Hello!" />
</VBox>
controller: HelloController.java
I bind the label's visibleProperty to model's member virable loggedIn, so I except the label will be invisible since initialized, and change while user clicked the button. But the label just display as "SAMPLE TEXT" and didn't react to any event.
package com.example.bind_test;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class HelloController {
@FXML
private Label welcomeText;
HelloModel model;
@FXML
public void initialize() {
welcomeText = new Label();
model = new HelloModel(false);
welcomeText.setText("SAMPLE TEXT");
welcomeText.visibleProperty().bind(model.loggedInProperty());
model.setLoggedIn(false);
// this label should be invisible after initialization
System.out.println("logged in:" + model.loggedInProperty());
}
@FXML
public void onHelloButtonClick() {
welcomeText.setText("TEXT Changed");
model.setLoggedIn(true);
System.out.println("logged in:" + model.loggedInProperty());
}
}
model: HelloModel.java
A model class base on MVC structure,
make the application can pass loggedIn virable between scenes.
package com.example.bind_test;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
public class HelloModel {
private BooleanProperty LoggedIn;
public boolean isLoggedIn() {
return LoggedIn.get();
}
public BooleanProperty loggedInProperty() {
return LoggedIn;
}
public void setLoggedIn(boolean loggedIn) {
this.LoggedIn.set(loggedIn);
}
public HelloModel() {
LoggedIn = new SimpleBooleanProperty(false);
}
public HelloModel(boolean loggedIn) {
LoggedIn = new SimpleBooleanProperty(loggedIn);
}
}
I'm confusing because the text label didn't react no matter how I changed virable with the button.
Console
logged in:BooleanProperty [value: false] // Running main()
logged in:BooleanProperty [value: true] // After clicking HelloButton
Does the way I binding properties wrong?
You create a Label
in your FXML file, and give it an fx:id
:
<Label fx:id="welcomeText" text="SAMPLE TEXT" />
and you initially inject that label into a field in the controller:
@FXML
private Label welcomeText;
But then in the initalize()
method you replace that field with a new label, which isn't part of the UI that is displayed:
welcomeText = new Label();
Consequently, everything else you do with welcomeText
(binding its visibility, changing its text, etc) operates on that new label you created, instead of the one defined in the FXML file and displayed in the scene.
Just remove the offending line:
// welcomeText = new Label();
It is always a mistake to initialize fields annotated @FXML
. The annotation means they will be initialized by the FXMLLoader
.