TextArea not updating in JavaFX - java

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.

Related

javafx choicebox to trigger a method onchange

Okay so i am very rusty on my java and even more on javafx. so i got a choicebox "categoryDrop" that when the value of the choicebox change i want to trigger this event that then takes the value of the choicebox and compare to an object "Folder" categorylist wich is an attribute it has.
here is my code
#FXML
private void folderByCategory(ActionEvent event) {
System.out.println("här1");
TreeItem<DocumentObject<?>> treeRoot = new TreeItem<>(new Folder());
for (Folder folder : logic.getFolderList()) {
if(f.getCategoryList().contains(categoryDrop.valueProperty())){
System.out.println("här2");
TreeItem<DocumentObject<?>> newFolders = new TreeItem<>(folder);
for(FileReference file : folder.getFileList()){
System.out.println(file.getName());
TreeItem<DocumentObject<?>> fileNode = new TreeItem<>(file);
newFolders.getChildren().add(fileNode);
}
treeRoot.getChildren().add(newFolders);
treeRoot.setExpanded(true);
}
treeNav.setRoot(treeRoot);
}
}
But then when i looked in scenebuilder i didnt see any good way to implement the method so it triggers when it changes. Anyone know a better way to do this? should i use a listener instead maybe?
ChoiceBox has an onAction property, so in FXML you can simply assign this controller method to this property:
<ChoiceBox fx:id="categoryDrop" onAction="#folderByCategory" />
Unfortunately, the current version of Scene Builder does not support this property, so you cannot set this directly from Scene Builder. There is a current issue filed for this.
Some workarounds are:
Edit the FXML manually to add the onAction attribute, as above.
Use a ComboBox instead of a ChoiceBox. The functionality is similar (though not identical) and a ComboBox will likely do what you need. Scene Builder does support the onAction property of a ComboBox.
Register the handler in the controller's initialize() method instead. All you need is
#FXML
private ChoiceBox<...> categoryDrop ;
public void initialize() {
categoryDrop.setOnAction(this::folderByCategory);
// existing code ...
}
#FXML
private void folderByCategory(ActionEvent event) {
// existing code...
}

JavaFx how to avoid creating one huge controller

I have an app in JavaFX, which has main scene with menu and toolbar, and smaller scenes, which are injected into this main scene, after one of menu buttons are being pressed.
Now, HomeCntroller is responsible for either scene components: Home Scene (with toolbar and menu), and injected scene. This leads me to create massive, huge and very unprofessional controller if number of injected scenes is more than one.
How to split controller responsibility?
Now my Controller looks like this:
changeDashboardPane method injects smaller Pane into my main HomePane.
#Component
#RequiredArgsConstructor(onConstructor = #__(#Autowired) )
public class HomeController extends AbstractController {
private static final Logger LOG = Logger.getLogger(HomeController.class);
private final BudgetProfileService budgetProfileService;
#FXML
private Label usernameLabel;
#FXML
private ComboBox<String> budgetProfilesComboBox;
#FXML
private AnchorPane dashBoardPane;
#FXML
public void initialize() {
refreshUsernameLabel();
getAllBudgetProfiles();
changeDashboardPane(PaneFactoryKeys.FINANCES_PANE);
}
private void refreshUsernameLabel() {
String username = UserAccountProvider.getLoggedUser().getUsername();
usernameLabel.setText(username);
}
private void getAllBudgetProfiles() {
List<String> budgetProfileNames = budgetProfileService.getAllBudgetProfileNames();
if (!budgetProfileNames.isEmpty()) {
budgetProfilesComboBox.getItems().clear();
budgetProfilesComboBox.getItems().addAll(budgetProfileNames);
}
}
#FXML
public void handleFinancesButtonAction() {
changeDashboardPane(PaneFactoryKeys.FINANCES_PANE);
}
#FXML
public void handlePeriodButtonAction() {
changeDashboardPane(PaneFactoryKeys.PERIOD_PANE);
}
#FXML
public void handleStatisticsButtonAction() {
changeDashboardPane(PaneFactoryKeys.STATISTICS_PANE);
}
#FXML
public void handleSettingsButtonAction() {
changeDashboardPane(PaneFactoryKeys.SETTINGS_PANE);
}
private final void changeDashboardPane(String paneFactoryKey) {
double injectedPanePosition = 0.0;
Pane paneToChange = getPaneFromFactory(paneFactoryKey);
dashBoardPane.getChildren().clear();
AnchorPane.setTopAnchor(paneToChange, injectedPanePosition);
dashBoardPane.getChildren().add(paneToChange);
}
}
To get this more clear, screens:
without injected second pane
with injected second pane
Any ideas guys?
I would recommend you to divide your main scene in smaller ones, for example you can have a tools scene, a header scene, a content scene and so on. Then you should have one controller for every scene.
After that I would use a publisher-subscriber pattern to deal with behaviors, like when you press a button on settings scene, it triggers an event that other scenes listen to and then they handle it changing their state accordingly.
I hope it was clear and can help!
Create multiple controllers , multiple FXML files - to continue on my answer that i provided you before, JavaFX how to inject new FXML content to current Scene each of those views that have separate fxml file also has
fx:controller="appplication.ExampleViewController"
attached to it.So what you do is create main controller as was mentioned , that is basically the FRAME CONTAINER that encapsulates controls to change your dynamic container.If your application is really ui rich and have a lot of functionality in one controller , you can break down your view even further:
For instance take out menu and put it into separated controller , and insert it into your main view with main controller
/same way as in method setView()/
, what you are doing is just taking it away to keep controller code smaller, YOU DONT DECREASE/INCREASE SCENE GRAPH THIS WAY, doesnt have a drawback its just a personal preference.
You gonna end up with more fxml files and controllers in the end.Its all the same thing as from your previous question there is no additional code needed you can actually reuse what was already provided.
Data between controllers are passed thru MODEL. - look more into MVC dont work with application data in controllers only care about view or passing them from/into model
To avoid a huge contoller class, as I am using multiple tabs, I split the tabs to single java files.
My solution was to create a cascade of classes:
Base: Containing all defs for FX types
Tab1 extends Base: Tab one implementation
Tab2 extends Tab1: Tab two implementation
Controller extends Tab2 implements Initializable: Implements initialize(URL url, ResourceBundle resourceBundle)
Important:
Any accessed object must be definded in the current tab or before.
Any Objects in Base are available in Controller whereas no object of Controller is accessable in Base, Tab1 or Tab2.
Feel free to add your opinion as comment or submit a improvement.

JavaFX FXML access succeed with lookup() method but not with #FXML annotation

I start exploring the JavaFX FXML application technology.
I use one main Stage accessed in Main class with Main.getStage() that is invoked in the start of application with the overriden method public void start(Stage stage). Having two public static Scene inside to keep the persistence while switching them.
#Override
public void start(Stage stage) throws Exception {
STAGE = stage;
LOGIN = new Scene(FXMLLoader.load(getClass().getResource("Login.fxml")));
REGISTER = new Scene(FXMLLoader.load(getClass().getResource("Register.fxml")));
STAGE.setScene(LOGIN);
STAGE.setTitle("FXApplication");
STAGE.show();
}
public static Stage getStage() {
return STAGE;
}
Both Scenes have the same controller class called MainController. Using:
Button with fx:id="buttonLoginRegister" to go to the REGISTER Scene
Button with fx:id="buttonRegisterBack" to go back to the LOGIN one.
and both having the same onClick event handleButtonAction(ActionEvent event). The TextFields are fields for a username to log in/register.
#FXML private Button buttonLoginRegister;
#FXML private Button buttonRegisterBack;
#FXML private TextField fieldLoginUsername;
#FXML private TextField fieldRegisterUsername;
#FXML
private void handleButtonAction(ActionEvent event) throws IOException {
Stage stage = Main.getStage();
if (event.getSource() == buttonLoginRegister) {
stage.setScene(Main.REGISTER);
stage.show();
// Setting the text, the working way
TextField node = (TextField) stage.getScene().lookup("#fieldRegisterUsername");
node.setText(fieldLoginUsername.getText());
// Setting the text, the erroneous way
// fieldRegisterUsername.setText(fieldLoginUsername.getText());
} else {
stage.setScene(Main.LOGIN);
stage.show();
}
}
My goal is to copy the value from the LOGIN TextField to the one in the REGISTER scene. It works well using the code above. However firstly I tried to access the element in the another Scene with:
fieldRegisterUsername.setText(fieldLoginUsername.getText());
And it's erroneous. To be exact, the fieldRegisterUsername is null.
Why are some elements found with the lookup(String id) method and not with #FXML annotation?
As mentioned in my comment, sharing a controller between different views is rarely a good idea, and I'd strongly advise you to make a separate controller for each view.
As to your problem itself - you have two instances of your controller class, one for each time you call FXMLLoader.load. Presumably, one view has the fieldLoginUsername TextField, while the other has fieldRegisterUsername.
If the condition of the if statement is met, it means the active scene was the Login scene, thus the controller handling it is the one which has fieldLoginUsername, so naturally fieldRegisterUsername will be null.
But on the first line inside the if clause you change the active scene to the Register one, so by the time you call scene#lookup you are referring to the scene whose controller is the Register controller, the one that does have fieldRegisterUsername.
If you were to call scene#lookup before changing the active scene you would find it returns null as well.
If you must use the same class for controller, you probably want to make sure you only have one instance of that class. That would necessitate using FXMLLoader#setController.

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.

JavaFX Line Chart [duplicate]

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.

Categories