Issue
I need to load fxml form to class object and manipulate it. Is it possible ? Currently I'm trying to do so:
<?xml version = "1.0" encoding = "UTF-8" ?>
<!-- imports -->
<?import javafx.geometry.Insets ?>
<?import javafx.scene.control.Label ?>
<?import javafx.scene.text.Font ?>
<?import org.bsoftware.parcel.domain.components.LogItem ?>
<!-- layout -->
<LogItem spacing = "3" xmlns:fx = "http://javafx.com/fxml" >
<padding>
<Insets bottom = "2" left = "5" right = "5" top = "2" />
</padding>
<Label fx:id = "labelTimestamp">
<font>
<Font name = "System Bold" size = "11" />
</font>
</Label>
<Label fx:id = "labelMessage">
<font>
<Font name = "System Italic" size = "11" />
</font>
</Label>
</LogItem>
@Getter
public class LogItem extends HBox
{
@FXML
private Label labelTimestamp;
@FXML
private Label labelMessage;
}
And I'm trying to access it like so
final LogItem logItem = FXMLLoader.load(Objects.requireNonNull(LogView.class.getResource("/fxml/components/log_item.fxml")));
But here i always get null pionter exeption
logItem.getLabelTimestamp()
Solution
Use the fx:root
construct as documented in:
Example App
Implementation notes:
- Uses the
fx:root
construct to link the FXML loaded nodes to a Java object. - Adds a CSS file to separate style from layout.
- Applies an MVC pattern, abstracting out a model object (
LogItem
) from a view object (LogItemView
). - Encapsulates the view elements (nodes) within the view, rather than exposing them via API.
- Exposes the LogItem as a property of the LogItemView, so that the log item can be queried directly for data and also replaced within the view if necessary.
- Uses a record for an immutable read-only LogItem rather than Lombok.
- Uses an Instant to represent a time.
- Uses a date formatter to format the instant as a date string in the default time zone.
- Uses a change listener to refresh the view if the logitem property associated with the view changes.
module-info.java
module com.example.logdemo {
requires javafx.controls;
requires javafx.fxml;
opens com.example.logdemo to javafx.fxml;
exports com.example.logdemo;
}
LogApplication.java
package com.example.logdemo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import java.time.Instant;
public class LogApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
LogItemView logItemView = new LogItemView();
logItemView.setLogItem(
new LogItem(Instant.now(), "hello, world")
);
stage.setScene(new Scene(logItemView));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
LogItem.java
package com.example.logdemo;
import java.time.Instant;
public record LogItem(
Instant timestamp,
String message
) {}
LogItemView.java
package com.example.logdemo;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import java.io.IOException;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
public class LogItemView extends HBox {
@FXML
private Label timestamp;
@FXML
private Label message;
private final ObjectProperty<LogItem> logItem = new SimpleObjectProperty<>();
private static final DateTimeFormatter formatter =
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
.withZone(ZoneId.systemDefault());
public LogItemView() throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(
LogItemView.class.getResource(
"log-item.fxml"
)
);
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
fxmlLoader.load();
logItem.addListener((observable, oldValue, newValue) -> {
if (newValue == null) {
timestamp.setText(null);
message.setText(null);
} else {
timestamp.setText(
formatter.format(
logItem.getValue().timestamp()
)
);
message.setText(
logItem.getValue().message()
);
}
});
}
public LogItem getLogItem() {
return logItem.get();
}
public void setLogItem(LogItem logItem) {
this.logItem.set(logItem);
}
public ObjectProperty<LogItem> logItemProperty() {
return logItem;
}
}
log-item.fxml
<?xml version = "1.0" encoding = "UTF-8" ?>
<?import javafx.scene.control.Label ?>
<?import com.example.logdemo.LogItemView?>
<?import java.net.URL?>
<fx:root type="com.example.logdemo.LogItemView" styleClass="log-item" xmlns:fx = "http://javafx.com/fxml">
<Label fx:id = "timestamp" styleClass="timestamp"/>
<Label fx:id = "message" styleClass="message"/>
<stylesheets>
<URL value="@log-item.css" />
</stylesheets>
</fx:root>
log-item.css
.log-item {
-fx-padding: 2px 5px 2px 5px;
-fx-spacing: 8px;
-fx-font-family: monospace;
-fx-font-size: 15px;
}
.log-item > .timestamp {
-fx-font-weight: bold;
}
.log-item > .message {
-fx-font-style: italic;
}
Answered By - jewelsea
Answer Checked By - Marilyn (JavaFixing Volunteer)