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.
Related
I have an FXML file with an empty Label named welcomeText.
It's the main Scene of my program and I would like to set the label text to something like that: Hello "username" when I start the program on Windows or Linux.
public class MainAdminController implements Initializable {
#FXML
private Label welcomeText;
final String username = System.getProperty("user.name");
#FXML
private void SetWelcome() {
welcomeText.setText("Hello " +username);
}
}
But it isn't show anything. Any idea how can I set the Label properly when I open the scene? Thanks.
The SetWelcome method is superfluous.
Define an initialize() method for your controller and it will automatically be invoked when the FXMLLoader loads a new document linked to the controller.
public void initialize() {
welcomeText.setText("Hello " +username);
}
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.
I have a JavaFx project I created using SceneBuilder.
I am also using a Guice plugin architecture.
I have one .fxml file that has a pane that I want to be the the content of another .fxml file.
Is there any easy way to link .fxml content from one file to another?
I have not used fx.guice plugin architecture before. Is there an easier way to this with plugin control?
Thanks!!
This was a big problem for us since we also are using Guice and JavaFX.
tl;dr I've attached some code at the bottom that we've been using for a year and a bit now without issue.
edit: I should've mentioned we did all this stuff before fx,guice existed, so this exists entirely outside that, and I probably should be using it.
Yes, but you will have to modify the view tree from java, and you must instantiate two controllers. If you're willing to let the fxml loader instantiate your controllers (we're not, more in a sec), then you simply need to have code along the lines of
solution 1:
loadMergedView(){
fxmlLoader.setLocation(getClass().getResource("/com/yourpkg/YourOuterView.fxml"));
Pane outerRoot = fxmlLoader.load();
fxmlLoader.setLocation(getClass().getResource("/com/yourpkg/YourInnerView.fxml"));
Pane innerView = fxmlLoader.load();
((Region)outerRoot.getChildren().get(2))...getChildren().add(innerView);
}
which isn't nice because it means the FX loader will try to create your controller for you, but you probably want guice to do that
luckily you can call setController (or setControllerFactory) to leverage guice, so now we have
solution 2:
#Inject private OuterController outerController
#Inject private InnerController innerController
loadMergedView(){
fxmlLoader.setLocation(getClass().getResource("/com/yourpkg/YourOuterView.fxml"));
//using the setControllerFactory instead of the setController
//means you can still declare the controller type in FXML
//which is good for our IDE intelliJ and general readability
fxmlLoader.setControllerFactory(() -> outerController);
Pane outerRoot = fxmlLoader.load();
fxmlLoader.setLocation(getClass().getResource("/com/yourpkg/YourInnerView.fxml"));
fxmlLoader.setControllerFactory(() -> innerController);
Pane innerView = fxmlLoader.load();
((Region)outerRoot.getChildren().get(2))...getChildren().add(innerView);
}
which is better but requires a 3rd party to load your components. What you really want, a le dependency-injection, is to have the child view be resolved first and as part of the resolution of the parent view.
For us, this brings us to
Solution 3:
class OuterController{
#FXML Pane rootPane;
#FXML Stuff otherStuffBoundInFXML;
#FXML AnchorPane innerContactPaneOne;
#Inject
public OuterController(InnerController inner, FXMLLoader loader){
loader.setControllerFactory(type -> this);
loader.setLocation(getClass().getResource("/com/yourpkg/YourOuterView.fxml"));
loader.load();
innerContactPaneOne.getChildren().add(inner.getRootView());
}
}
class InnerController{
#FXML Pane innerContentPaneTwo; //this will be a child of PaneOne in OuterController
#FXML Button otherStuff;
#Inject
public InnerController(FXMLLoader loader){
loader.setControllerFactory(type -> this);
loader.setLocation(getClass().getResource("/com/yourpkg/YourInnerVIew.fxml"));
loader.load();
}
public Node getRootView(){
return innerContentPaneTwo;
}
}
//with somebody calling
OuterController rootController = injector.getInstance(OuterController.class);
And finally, in the name of 'convention over configuration', we created a few classes (attached below) that attempt to 'automatically' find the view by reflecting on the controllers name (eg OuterController) and assuming that it will find an FXML view view in the same directory as the controller's class file with the word controller replaced with view (eg OuterView.fxml)). We also leveraged a neat trick in super-ctor-order in java to allow us to have pre-setup FXML values.
So now we get:
solution4 :
class OuterController extends PreloadedFX{
#FXML Pane rootPane;
#FXML Stuff otherStuffBoundInFXML;
#FXML AnchorPane innerContactPaneOne;
#FXML Checkbox importantCheckbox;
#FXML Label importantLabel;
// because of 'PrealoadedFX' getting called first,
// you can actually inline initialize object constants
// like this
private final ObservableBooleanValue isSelected = importantCheckbox.selectedProperty();
// or using an initializer
{
int x = 4;
importantLabel.setText(importantLable.getText() + x);
}
#Inject
public OuterController(InnerController inner, FXMLLoader loader){
super(loader);
innerContactPaneOne.getChildren().add(inner.getRootView());
}
}
class InnerController extends PreloadedFX{
#FXML Pane innerContentPaneTwo; //this will be a child of PaneOne in OuterController
#FXML Button otherStuff;
#Inject
public OuterController(FXMLLoader loader){
super(loader);
}
public Node getRootView(){
return innerContentPaneTwo;
}
}
//with somebody calling
OuterController rootController = injector.getInstance(OuterController.class);
you can get the source code for the PreloadedFX and View-By-Convention code here:
https://gist.github.com/Groostav/ff35eb2d19b348f2e25c
which is as elegant as I've been able to make this particular union of frameworks.
Hope that helps!
I'm defining the TextArea in my controller class like this:
#FXML
private TextArea txtAreaStatus;
And I'm trying to append text to the TextArea using this code:
#FXML
public void clickGo (ActionEvent event) {
txtAreaStatus = new TextArea("");
txtAreaStatus.appendText("data");
System.out.println("clicked");
}
I'm really confused as to why my text area is not updating. No errors whatsoever.
When I click the button, clicked gets printed on the screen.
What am I doing wrong?
Whenever you are using FXML and Controller combination, controls references are annotated with #FXML in the controller. The objects are injected into their respective references when the fxml is loaded. Therefore you don't need to do define a new object for them.
In your code you need to remove :
txtAreaStatus = new TextArea("");
because this makes you loose the reference to the object of TextField on the scene and defines a new Textfield object (which is not on the scene). You are later trying to do operations on this new object.
I have a class Foo which just load the FXML and create the scene.
In the FXML, I set the controller to be FooController (fx:controller="FooController")
And I add a MenuButton:
<MenuButton fx:id="menuButton" layoutX="264.1875" layoutY="146.5" mnemonicParsing="false" text="MenuButton" />
And I try to set the menuButton in the FooController:
public class FooController implements Initializable{
#FXML
final MenuButton menuButton = new MenuButton("Modalities");
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
final ObservableList<CheckMenuItem> listFilter = FXCollections.observableArrayList();
final MenuButton menuButton = new MenuButton("Modalities");
CheckMenuItem item1 = new CheckMenuItem("T1");
CheckMenuItem item2 = new CheckMenuItem("T1C");
CheckMenuItem item3 = new CheckMenuItem("T2");
listFilter.addAll(item, item2, item3);
menuButton.getItems().addAll(listFilter);
menuButton.setId("menuButton");
}
}
But despite of setting everything for the MenuButton it doesn't display any of the CheckMenuItems in the GUI.
How can I load those items in menuButton defined in the FXML?
Never set an #FXML initialized value to a new value.
In your posted code, you are doing this twice, when you should not be doing it at all.
The FXMLLoader will create new items within the hierarchy of the component it instantiates and inject references to these new items into your controller. If you then set these references to new values, the new values will never be included in the displayed component hierarchy unless you specifically add them there, which kind of defeats the purpose of using FXML in the first place as it ignores things defined in your FXML file.
What you should have is:
public class FooController {
#FXML
MenuButton menuButton;
public void initialize() {
menuButton.getItems().addAll(
FXCollections.observableArrayList(
new CheckMenuItem("T1"),
new CheckMenuItem("T1C"),
new CheckMenuItem("T2")
)
);
}
}
Also note that if your CheckMenuItems in the above code are static rather than a dynamic list, then you could just define them all in your FXML document instead of creating them in code.
Note: This is a duplicate question and has been asked before (not the exact question, but the substance of it), but my google skills couldn't dig up some of the duplicates.