Why isn't a TreeView root shown in this FXML app? - java

I'm working on a FXML app and I have ran into this error I can't see to resolve: The TreeViews produced by the attached code do not display their roots, even though to my best knowledge they should(the TreeViews themselves do show). Buttons and labels work fine.
On the off note, what exactly is the initialize() method supposed to do? Currently, I just write stuff in there sort of like they did in a tutorial and hope it shows up somehow. All I found about that method was the documentation on the Initializable interface, which didn't make me much wiser. Something about location and resources, but what exactly does that means in terms of Java? Those terms are too general to google for.
Also, the code is copied over from an earlier app that used Javafx only, with no FXML, and it wored fine there. So why doesn't it work now?
package sample;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import java.io.IOException;
public class FolderSyncerMainWindowController {
final String FOLDER_SYNCER = "FolderSyncer";
final String BROWSE = "Browse";
final String SOURCE = "Source...";
final String TARGET = "Target...";
final String COMPARE = "Compare";
final String CANCEL = "Cancel";
final String SYNCHRONIZE = "Synchronize";
final String COMPARING = "Comparing, this may take several minutes.";
final String SYNCHRONIZING = "Synchronizing, this may take several minutes.";
final String DONE = "Done.";
#FXML
private Label sourceLabel;
#FXML
private Label targetLabel;
#FXML
private Button sourceBrowseButton;
#FXML
private Button targetBrowseButton;
#FXML
private Button compareButton;
#FXML
private Button synchronizeButton;
#FXML
private TreeView sourceTreeView;
#FXML
private TreeView targetTreeView;
#FXML
private TreeItem sourceTreeViewRoot;
#FXML
private TreeItem targetTreeViewRoot;
public void initialize() {
sourceLabel.setText(SOURCE);
targetLabel.setText(TARGET);
sourceBrowseButton.setText(BROWSE);
targetBrowseButton.setText(BROWSE);
compareButton.setText(COMPARE);
synchronizeButton.setText(SYNCHRONIZE);
sourceTreeViewRoot = new TreeItem<>();
targetTreeViewRoot = new TreeItem<>();
sourceTreeViewRoot.setExpanded(true);
targetTreeViewRoot.setExpanded(true);
sourceTreeView = new TreeView<>(sourceTreeViewRoot);
targetTreeView = new TreeView<>(targetTreeViewRoot);
sourceTreeView.setShowRoot(true);
targetTreeView.setShowRoot(true);
}
}

There are several identical question to this on this forum, but I can't find them with a quick search.
It is always an error to initialize a #FXML-annotated field. In other words, you should never write code like
#FXML
private TreeView sourceTreeView ;
// ...
sourceTreeView = new TreeView<>(...);
The #FXML annotation means the field refers to an object created by the FXMLLoader as part of parsing the fxml file; that object is added to the scene graph. If you then reassign the reference with sourceTreeView = new TreeView..., then the reference no longer point to the tree view that is part of the scene graph.
Instead, do
sourceTreeView.setRoot(sourceTreeViewRoot);
As an aside, you should never use raw types (TreeView) but should use an appropriate generic type: TreeView<Something>, where Something is the type of the value in each tree item.

Related

Are there alternatives to javafx.fxml.Initializable to run code at the start of a scene?

The main goal is to run code when the scene is loaded and I know Initializable is the recommended solution and I've been using it except the problem I'm running into is that it's not conducive to integration testing.
The logic in my initialize method is dependent on previous scenes, so when I load up just this one screen, I get some NullPointer exceptions because some things I expected to exist do not exist.
There is no way for me to mock any of this because, using TestFX, the initialize method is run on the first line of setup, FXMLLoader loader = new FXMLLoader(getClass().getResource("test.fxml"));.
From there, I would have done loader.getController(); to get access to the generated controller and mock certain class fields and stuff, but the issue is that I can't do this before the initialize method runs. It's a catch 22 situation because to get access to the controller javafx.fxml.FXMLLoader must run, but when that runs it automatically executes the initialize method, which I would otherwise need to mock parts of, or generate expected information.
So, what are my options? Is there a way to run code at the start of a scene without Initializable?
public class GameScreenController implements Initializable {
private AppService appService;
private PlayerService playerService;
private DirectionService directionService;
private RoomDirectionService roomDirectionService;
#FXML
private javafx.scene.control.Button closeButton;
#FXML
private Label goldAmount;
#FXML
private ImageView player;
private final BooleanProperty wPressed = new SimpleBooleanProperty(false);
private final BooleanProperty aPressed = new SimpleBooleanProperty(false);
private final BooleanProperty sPressed = new SimpleBooleanProperty(false);
private final BooleanProperty dPressed = new SimpleBooleanProperty(false);
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
this.appService = new AppService();
this.directionService = new DirectionService();
this.roomDirectionService = new RoomDirectionService(this.directionService);
this.goldAmount.setText(String.valueOf(this.appService.getPlayerState().getGoldAmount()));
this.playerService = new PlayerService(this.player, this.appService,
this.roomDirectionService);
this.playerService.moveX(this.appService.getPlayerState().getSpawnCoordinates()[0]);
this.playerService.moveY(this.appService.getPlayerState().getSpawnCoordinates()[1]);
...

Problem with extending SimpleStringProperty

I am just trying to extend a SimpleStringProperty in OpenJFX 11.0.1 to add some extra functionality. But ist seems not so easy, I experienced strange behavior of my extended Property and I don't know why. I think it should work.
My in this sample code simplified SimpleStringProperty extension contains another readonly string property which should be updated every time the the user types into a bound TextField. In this case remove all not allowed characters and convert the prefix. (I know this is not perfect but short enough to show)
After starting the sample code you will get a window with a rows of Controls. Typing in a String like "001 (242) 555666" the label should show the normalized phone number like "+1242555666".
The initial conversion works correcty.
I never get any exceptions.
The conversion is called when I type in new digits.
But if you play around with typing and deleting after a few seconds the set() method of my property isn't longer triggered by the bidirectional binding to the TextField.
To simplify the example I didn't use a TextFormatter. If I use one the problem doesn't change.
Can anyone help me figure out the problem?
Windows and OS X show the same behavior with OpenJFX 11 and OpenJFX 11.0.1
I tried the same code with JDK 1.8 and there it works fine.
package testproperty;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
public class TestProperty extends Application {
// attempt to create an own property
public class myPhoneNumberProperty extends SimpleStringProperty {
private final ReadOnlyStringWrapper normalizedNumber = new ReadOnlyStringWrapper("");
public ReadOnlyStringProperty normalizedNumberProperty() { return normalizedNumber.getReadOnlyProperty(); }
public String getNormalizedNumber() { return normalizedNumber.get(); }
public myPhoneNumberProperty() {
super();
}
public myPhoneNumberProperty(String s) {
super(s);
calculate();
}
#Override
public void set(String s) {
super.set(s);
calculate();
}
private void calculate() {
// some calculations (only for test purposes)
String original = this.get();
String result = original.replaceAll("[^0123456789]","");
if (result.startsWith("00")) result = result.replaceFirst("00", "+");
if (original.startsWith("+")) result = "+".concat(result);
normalizedNumber.set(result);
}
}
#Override
public void start(Stage primaryStage) {
// create my property
myPhoneNumberProperty phoneNumberA = new myPhoneNumberProperty("+34 952 111 222");
// set up grid pane
GridPane grid = new GridPane();
grid.setPadding(new Insets(5,5,5,5));
grid.setVgap(20);
grid.setHgap(20);
// set up the row
Label labelA = new Label("Enter phone number");
TextField textFieldA = new TextField();
textFieldA.textProperty().bindBidirectional(phoneNumberA);
Label labelB = new Label("Normalized number");
Label labelN = new Label();
labelN.textProperty().bind(phoneNumberA.normalizedNumberProperty());
grid.addRow(0, labelA, textFieldA, labelB, labelN);
// complete scene
Scene scene = new Scene(grid, 1000, 100);
primaryStage.setTitle("PhoneNumberProperty TestProg");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Your phoneNumberA property object is being garbage collected. To fix this you must keep a strong reference to the object. One option is to make it an instance field.
JavaFX implements bindings using weak listeners/references. Bidirectional bindings have no strong references to the other property. This is different from unidirectional bindings where a reference to the observable value must be kept in order to unbind from it later.

Java - What is the best way to make a program load options from a file?

I've been teaching myself JavaFX and I'm developing a simple file transfer calculator. It calculates file sizes, transfer speeds and transfer times.
I want to be able to load my current options from a file (eg. Megabytes, Gigabytes etc) to several JavaFX ChoiceBoxes. I need:
File Sizes
Transfer Speeds
Time Units
And I need to have information on how they convert since the user might want to know how much time in seconds it would take to transfer 1 TB of data through a 42 KB/s connection.
I thought of using a text file but it would be too much trouble formating in a way easy enough to be read by the file and it would be hard to automate a writing process. I thought of using an XML to do so but I have no idea of how to do use it for this purpose or if it would be a good idea. So, what would be the best way to load the options and the information on each one? And how to use it?
package application;
import java.io.ObjectInputStream.GetField;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextField;
import javafx.scene.control.Alert.AlertType;
public class MainInterfaceController implements Initializable {
/*
* Declarations of the interface interactive bits FS - File Size TS -
* Transfer Speed TT - Transfer Time
*/
#FXML
private TextField FSTextField;
#FXML
private ChoiceBox<String> FSChoiceBox;
#FXML
private RadioButton FSRadioButton;
#FXML
private TextField TSTextField;
#FXML
private ChoiceBox<String> TSChoiceBox;
#FXML
private RadioButton TSRadioButton;
#FXML
private TextField TTTextField;
#FXML
private ChoiceBox<String> TTChoiceBox;
#FXML
private RadioButton TTRadioButton;
#FXML
private Button AboutButton;
#FXML
private Button CalculateButton;
// Variables & Data Sets
//These should be initialized in the init method so they can be populated with info from a file(?)
private ObservableList<String> fileSizes = FXCollections.observableArrayList("KB", "MB", "GB", "TB", "PB");
private ObservableList<String> transferSpeeds = FXCollections.observableArrayList("KB/s", "MB/s", "GB/s", "TB/s", "PB/s");
private ObservableList<String> timeUnits = FXCollections.observableArrayList("Seconds", "Minutes", "Hours");
#Override
public void initialize(URL location, ResourceBundle resources) {
populateChoiceBoxes();
setRadioButtonsActions();
CalculateButton.setOnAction(e -> calculateValues());
}
private void populateChoiceBoxes() {
FSChoiceBox.setItems(fileSizes);
TSChoiceBox.setItems(transferSpeeds);
TTChoiceBox.setItems(timeUnits);
}
private void setRadioButtonsActions() {
FSRadioButton.setOnAction(e -> clearRadioButtons(e));
TSRadioButton.setOnAction(e -> clearRadioButtons(e));
TTRadioButton.setOnAction(e -> clearRadioButtons(e));
}
private void clearRadioButtons(ActionEvent e) {
if (e.getSource() == FSRadioButton) {
TSRadioButton.setSelected(false);
TTRadioButton.setSelected(false);
}
if (e.getSource() == TSRadioButton) {
FSRadioButton.setSelected(false);
TTRadioButton.setSelected(false);
}
if (e.getSource() == TTRadioButton) {
FSRadioButton.setSelected(false);
TSRadioButton.setSelected(false);
}
}
private void calculateValues() {
if (FSRadioButton.isSelected()) {
try {
String TSText = TSTextField.getText();
double transferSpeed = Double.parseDouble(TSText);
String TTText = TTTextField.getText();
double transferTime = Double.parseDouble(TTText);
} catch (NumberFormatException e) {
Alert alert = new Alert(AlertType.ERROR, "The transfer speed/transfer time must be a number!");
alert.showAndWait();
}
}
}
private void convertSpeed(double transferSpeed, ChoiceBox<String> speedUnit) {
}
}
So far I have the code above. As you can see in the beggining I declare ObservableLists. That's what I want to automate and the final method for conversion. This would way the program would be more flexible and easily updatable.
The standard way to do this in Java is with a .properties file, which marries up with class java.util.Properties
There is a standard format of either:
key=value
<entry key="key">value</entry>
The Properties class has standard methods for loading the properties in and accessing them from the Properties object.
Referencing the J2SE Javadoc an example might be:
Properties properties = new Properties();
try(InputStream is = this.getClass().getResourceAsStream("my.properties")) {
properties.load(is);
}
String value = properties.getProperty("key");
There are two different library's I would recommend for storing data in java. The first is json-simple its simple and easy to use and there is little setup required to get it working you can find it here there are also a lot of tutorials. Another option is to use yaml. The library snake yaml is really good for this and again is easy to setup and there are many tutorials online. you can find it here.

How to make a JavaFX object appear in FXML?

In the controller class I have the following:
final String SOURCE = "Source...";
private Label sourceLabel = new Label(SOURCE);
In the FXML, I try to make this label appear by:
<Label fx:id="sourceLabel" prefHeight="17.0" prefWidth="44.0">
Why is the label in the FXML preview empty instead of appearing as specified, i.e.: writing out the string "Source..."? Is there something I don't get in the syntax or some additional housekeeping/technicality I need to do?
In your controller write:
final String SOURCE = "Source...";
#FXML
private Label sourceLabel;
public void initialize() {
sourceLabel.setText(SOURCE);
}
I.e., use the #FXML notation to link the controller sourceLabel reference to the Label defined by the sourceLabel fx:id in the FXML. Use the initialize method to initialize the sourceLabel data.

How to use javafx.beans.binding.Bindings.select(...) for concise value binding

Overview
As a Swing developer of ten years, I've been thrilled with the features introduced with JavaFX 2.0, especially the rich, fluent, high-level data-binding facilities. This facility alone is worth the cost of learning a new API (which is much less since abandoning FX script). It's going to have a direct impact on the readability and maintainably of my model/view synchronization code.
So far I'm having great success at first level and basic derived bindings, but am struggling to figure out the "JavaFX way" of binding one value to a value two or more levels of indirection in the data graph.
Problem
As shown in the code example below, I'm attempting to use javafx.beans.binding.Bindings.select() to synchronize the text value of a Label with one of the contained properties of the currently selected item in a ComboBox. This code is a simple example of something more complex I'm trying to do, so I understand that it's not hard to do this with the lower level bindings API. I'd like to know if it's possible with the higher-level fluent API, and if the select(...) method actually tracks changes in the indirect properties (i.e. update property if either the direct property or the selected subproperty change).
The documentation and examples on select(...) are sparse, so I'm hoping someone with advanced experience with this can tell me if I'm trying to use the API as designed, or if there's another way to use the high-level binding API to do what I want.
Sample Code
Here's the demo code. When run, there's a ComboBox with two items in it, and then two labels. The first label shows the toString() version of the selected item. The second label attempts to display one of the properties of the selected item, but only displays null.
import static javafx.beans.binding.Bindings.*;
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/** Testing cascading binding change triggers. */
public class SandboxTest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
VBox root = new VBox(8);
root.setStyle("-fx-padding: 8;");
Scene s = new Scene(root);
stage.setWidth(200);
stage.setScene(s);
ComboBox<MoPoJo> list = new ComboBox<SandboxTest.MoPoJo>();
list.itemsProperty().set(FXCollections.observableArrayList(new MoPoJo("foo", "bar"), new MoPoJo("baz", "bat")));
Label direct = new Label();
direct.setTooltip(new Tooltip("Selected item to string"));
Label withSelect = new Label();
withSelect.setTooltip(new Tooltip("Second property of selected item"));
direct.textProperty().bind(convert(list.getSelectionModel().selectedItemProperty()));
withSelect.textProperty().bind(convert(select(list.getSelectionModel().selectedItemProperty(), "two")));
root.getChildren().addAll(list, direct, withSelect);
stage.show();
}
private static class MoPoJo {
private StringProperty _one = new SimpleStringProperty();
private StringProperty _two = new SimpleStringProperty();
private StringProperty _name = new SimpleStringProperty();
public MoPoJo(String o, String t) {
_one.set(o);
_two.set(t);
_name.bind(format("{ %s, %s }", oneProperty(), twoProperty()));
}
public StringProperty oneProperty() {
return _one;
}
public StringProperty twoProperty() {
return _two;
}
public ReadOnlyStringProperty nameProperty() {
return _name;
}
#Override
public String toString() {
return nameProperty().get();
}
}
}
Bindings.select can't access private class. Make MoPoJo a public class and your code will work.
public static class MoPoJo {
P.S: I believe that fact worth to be mentioned in docs, so I filed http://javafx-jira.kenai.com/browse/RT-20640 on JavaFX javadoc.

Categories