I have just started learning java fx and I am trying to get user input from two text field. Once they click the button this will be displayed on a console.
However, I am keep getting an error and cannot figure out why.
I have assigned the 'handle' function using Scenebuilder, the error is pointing at the method.
Main Class:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller:
package sample;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
public class Controller {
public TextField userField;
public TextField passField;
public Button logButton;
public void handle(ActionEvent event) {
String username = userField.getText();
String passw = passField.getText();
System.out.printf("Logged in as %s %s", username, passw);
}
}
although you didn't show the error, but i think it's because you didn't annotate the fields userField and passwordField with #FXML annotation
by this annotation you tie the fields in the controller with fields in the fxml
so to solve this problem let's do the following simple steps
public class Controller implements Initializable{
#FXML
private TextField userField;
#FXML
private TextField passField;
#FXML
private Button logButton;
private void handle(ActionEvent event)
{
System.out.println(userField.getText());
}
#Override
public void initialize(URL url, ResourceBundle rb)
{
logButton.setOnAction(this::handle);
}}
and in the scene builder follow this image
try this,and if you still have problems just let a comment (:
As you are using Scene Builder, you should use the sample controller skeleton: Under View, select Use Sample Controller Skeleton. Also, specify the "On Action" for the text field. Scene Builder will dot the i's and cross the t's for you.
Related
I'm pretty new to JavaFX. I have learnt how to switch scenes between FXML files or classes purely coded in JavaFX only.
My biggest challenge now is to switch between an FXML UI and another built in JavaFX or vice-versa, so I know it's possible but I just can't get it right. My code for my Application controller is:
package com.example.fxmltojavafx;
import com.sun.javafx.stage.EmbeddedWindow;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import java.net.URL;
import java.util.ResourceBundle;
public class HelloController extends Application {
#FXML
private Label welcomeText;
#FXML
private Button bt_switch;
#FXML
protected void onHelloButtonClick() {
//switch scene method from fxml to pure javafx
//bt_switch.setOnAction(e -> window.setScene(InterfaceSwitch));
welcomeText.setText("Welcome to JavaFX Application!");
}
#Override
public void start(Stage stage) throws Exception {
bt_switch.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
bt_switch.setOnAction(e -> window.setScene(InterfaceSwitch));
}
});
}
}
How do I reference the JavaFX class I want to load from a click of a fxml button? I tried window.setScene(), but it doesn't get my reference right. Of course I can't use FMXMLLoader.load().
The scene I want to load from this button is coded in this class:
(The buttons coded into it are just a test)
package com.example.fxmltojavafx;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/***
* Intefaccia puramente in JavaFX
*/
public class InterfaceSwitch extends Application {
private Stage stage;
#Override
public void start(Stage stageInterface) throws Exception {
VBox parent = new VBox();
parent.getChildren().add(new Label("SERVER DOMINATOR : Inserisci dati"));
//setup process
HBox username = new HBox(new Label("username :")); //username field
TextField usernametext = new TextField();
username.getChildren().add(usernametext);
parent.getChildren().add(username);
HBox password = new HBox(new Label("password :")); //password field
TextField passwordtext = new TextField();
password.getChildren().add(passwordtext);
parent.getChildren().add(password);
HBox port = new HBox(new Label("port :")); //port field
TextField porttext = new TextField();
port.getChildren().add(porttext);
parent.getChildren().add(port);
HBox signupQuest = new HBox(new Label("Set up my game!"));
parent.getChildren().add(signupQuest);
Button button1 = new Button("Set up");
parent.getChildren().add(button1);
//Scene scene = new Scene(new Label("Server Dominator : Set up"));
stage.setScene(new Scene(parent));
stage.show();
}
}
My FXML UI is basically just 2 buttons, one for "hello world" and the other is the bt_switch
My main class:
package com.example.fxmltojavafx;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
It must be a simple set up issue but I can't get my head around it. How can I do it?
The Application class represents the entire application, and specifically its lifecycle. Its purpose is to manage the startup and shutdown of the application via methods like start(), init(), and stop(). Consequently, each application should have only one Application subclass and there should only be one instance of it (which is created for you when the application is launched).
In the code you posted, HelloApplication represents the application lifecycle. HelloController is just a controller, and should not be a subclass of Application. InterfaceSwitch does not represent the application either (it just represents a UI), so it should not be a subclass of Application.
You should have something like
public class SomeUI {
private VBox root ;
public SomeUI() {
root = new VBox();
root.getChildren().add(new Label("SERVER DOMINATOR : Inserisci dati"));
//setup process
HBox username = new HBox(new Label("username :")); //username field
TextField usernametext = new TextField();
username.getChildren().add(usernametext);
root.getChildren().add(username);
HBox password = new HBox(new Label("password :")); //password field
TextField passwordtext = new TextField();
password.getChildren().add(passwordtext);
root.getChildren().add(password);
HBox port = new HBox(new Label("port :")); //port field
TextField porttext = new TextField();
port.getChildren().add(porttext);
root.getChildren().add(port);
HBox signupQuest = new HBox(new Label("Set up my game!"));
root.getChildren().add(signupQuest);
Button button1 = new Button("Set up");
root.getChildren().add(button1);
}
public Parent getRoot() {
return root ;
}
}
Since you said your controller is a controller for an FXML file with two buttons, it needs two event handler methods (one for each button). The handler for the button which switches scenes just does so in the normal way. Obviously, the controller should not be an Application.
public class HelloController {
#FXML
private Label welcomeText;
#FXML
protected void onHelloButtonClick() {
welcomeText.setText("Welcome to JavaFX Application!");
}
#FXML
protected void switchScenes() {
SomeUI newScene = new SomeUI();
Parent root = newScene.getRoot();
welcomeText.getScene().setRoot(root);
}
}
The FXML would look something like:
<VBox spacing="10" xmlns = "http://javafx.com/javafx/16" xmlns:fx = "http://javafx.com/fxml/1" fx:controller="com.example.HelloController">
<Label fx:id="welcomeText" />
<Button text="Say Hello" onAction="#onHelloButtonClick"/>
<Button text="Switch scenes" onAction="#switchScenes" />
</VBox>
I'm trying to input user data from TextField in new window into TableView in the main window. But it is not populating table, and I'm not getting any Exception. On button clicked new window just closes. I'm creating very simple app just to figure out how to communicate properly between controllers.
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
static Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
this.primaryStage= primaryStage;
this.primaryStage.setTitle("Hello World");
this.primaryStage.setScene(new Scene(root));
this.primaryStage.show();
}
public void closeStage(){
primaryStage.close();
}
public static void main(String[] args) {
launch(args);
}
}
Controller class for main window where are table and button for opening new Stage:
package sample;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
#FXML TableView<radnici> tabela;
#FXML TableColumn<radnici, String> kolona;
#FXML Button dodajRadnikaDugme;
#FXML Button closeDugme;
Main main = new Main();
static Stage stage = new Stage();
Scene scene;
BorderPane borderPane;
#Override
public void initialize(URL location, ResourceBundle resources) {
kolona.setCellValueFactory(new PropertyValueFactory<radnici, String>("ime"));
}
public TableView<radnici> getTabela() {
return tabela;
}
public TableColumn<radnici, String> getKolona() {
return kolona;
}
//Opening new Stage on button clicked
public void dodajRadnikaDugemKlik() throws IOException {
FXMLLoader loader = new FXMLLoader();
borderPane = new BorderPane();
loader.setLocation(getClass().getResource("dodajRadnika.fxml"));
borderPane = loader.load();
scene = new Scene(borderPane);
stage.setTitle("Dodaj Radnika");
stage.setScene(scene);
stage.showAndWait();
}
//Closing main window
public void closeDugmeKlik(){
main.closeStage();
}
//Method for closing new window
public void CloseStage(){
stage.close();
}
}
Controller class for the new Window where are just TextField and Button:
package sample;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import java.io.IOException;
public class dodajRadnikaKontroler {
#FXML TextField upišiRadnika;
#FXML Button dodajRadnika;
BorderPane borderPane;
public void initialize(){
System.out.println("učitavanje podataka...");
}
//Method for adding data on button clicked from TextField into table in main window
#FXML public void dodajRadnikaKlik() throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("sample.fxml"));
borderPane = new BorderPane();
borderPane = loader.load();
Controller controller = loader.getController();
ObservableList<radnici> lista = FXCollections.observableArrayList();
lista.add(new radnici(upišiRadnika.getText()));
controller.tabela.setItems(lista);
upišiRadnika.clear();
controller.CloseStage();
}
}
Model Class for workers ("radnici"):
package sample;
public class radnici {
private String ime;
public radnici(String ime) {
this.ime = ime;
}
public String getIme() {
return ime;
}
public void setIme(String ime) {
this.ime = ime;
}
}
Please can someone help me so that I can finally do this properly.
Note: please use proper naming conventions. I have changed some names of classes so that they follow standard conventions.
You don't see the changes to your table, because you are loading a entire new UI structure from the FXML file, including a new TableView, and then you set the items in that new table view.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("sample.fxml"));
borderPane = new BorderPane();
// load a new UI from the FXML file:
borderPane = loader.load();
// get the controller for the new UI:
Controller controller = loader.getController();
ObservableList<radnici> lista = FXCollections.observableArrayList();
lista.add(new radnici(upišiRadnika.getText()));
// set the table items in the new UI:
controller.tabela.setItems(lista);
upišiRadnika.clear();
controller.CloseStage();
Since you don't even display this new UI, you don't see the items in the table.
Presumably what you actually want to do is update the items in the existing table. All you need for this is a reference to the table's backing list. You need to pass that to the controller for the second FXML file. Add a field and method to DodajRadnikaKontroler:
public class DodajRadnikaKontroler {
// Existing code omitted:
private ObservableList<Radnici> items ;
public void setItems(ObservableList<Radnici> items) {
this.itmes = items ;
}
}
and then pass the table's list to the new controller when you load the FMXL file:
//Opening new Stage on button clicked
public void dodajRadnikaDugemKlik() throws IOException {
FXMLLoader loader = new FXMLLoader();
borderPane = new BorderPane();
loader.setLocation(getClass().getResource("dodajRadnika.fxml"));
borderPane = loader.load();
DodajRadnikaKontroler controller = loader.getController();
controller.setItems(tabela.getItems());
scene = new Scene(borderPane);
stage.setTitle("Dodaj Radnika");
stage.setScene(scene);
stage.showAndWait();
}
and now in the dodajRadnikaKlik() method you just update the list:
#FXML public void dodajRadnikaKlik() throws IOException {
// if you want to add an item:
items.add(new radnici(upišiRadnika.getText()));
// if you want to replace all items with the new one:
// items.setAll(new radnici(upišiRadnika.getText()));
upišiRadnika.clear();
controller.CloseStage();
}
I'm making an applet using JavaFX 8 (via a JFXPanel) and was planning on using a Popup to display an area with a TextField in it. This works as expected, until the application loses focus. After that, I'm unable to make the TextField regain focus so you can't type in it any more. I've tried calling requestFocus() on the TextField but that didn't seem to do anything. The problem can be seen in the simple example below:
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.stage.Popup;
import javax.swing.*;
public class FXApplet extends JApplet {
protected Scene scene;
protected Group root;
#Override
public final void init() {
SwingUtilities.invokeLater(() -> initSwing());
}
private void initSwing() {
JFXPanel fxPanel = new JFXPanel();
add(fxPanel);
Platform.runLater(() -> {
initFX(fxPanel);
initApplet();
});
}
private void initFX(JFXPanel fxPanel) {
root = new Group();
scene = new Scene(root);
fxPanel.setScene(scene);
}
public void initApplet() {
Popup popup = new Popup();
popup.setAutoHide(false);
popup.getContent().add(new TextField());
popup.show(scene.getWindow());
}
}
If you create a binding between a JavaFX TextField and a property, then this binding is invalidated on every keystroke, which causes a change to the text.
If you have a chain of bindings the default behavior could cause problems, because in the middle of the editing values may be not valid.
Ok, I know I could create an uni-directional binding from the property to the textfield and register a change listener to get informed when the cursor leaves the field and update the property manually if necessary.
Is there an easy, elegant way to change this behavior so that the binding is only invalidated when the editing is complete, e.g. when the cursor leaves the field?
Thanks
I think you've pretty much described the only way to do it. Here's about the cleanest way I can see to implement it (using Java 8, though it's easy enough to convert the lambdas back to be JavaFX 2.2 compatible if you need):
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CommitBoundTextField extends Application {
#Override
public void start(Stage primaryStage) {
TextField tf1 = new TextField();
createCommitBinding(tf1).addListener((obs, oldText, newText) ->
System.out.printf("Text 1 changed from \"%s\" to \"%s\"%n", oldText, newText));
TextField tf2 = new TextField();
createCommitBinding(tf2).addListener((obs, oldText, newText) ->
System.out.printf("Text 2 changed from \"%s\" to \"%s\"%n", oldText, newText));
VBox root = new VBox(5, tf1, tf2);
Scene scene = new Scene(root, 250, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
private StringBinding createCommitBinding(TextField textField) {
StringBinding binding = Bindings.createStringBinding(() -> textField.getText());
textField.addEventHandler(ActionEvent.ACTION, evt -> binding.invalidate());
textField.focusedProperty().addListener((obs, wasFocused, isFocused)-> {
if (! isFocused) binding.invalidate();
});
return binding ;
}
public static void main(String[] args) {
launch(args);
}
}
I realize that I am a little late with a response, but thought this might be useful to someone.
When using TextFields, I often attach a TextFormatter to help validate entries. You can attach a listener to the formatters' valueProperty. That property is updated when the text is committed, rather than on every keystroke.
Here's an example of what I am talking about using a TextField specialized for integer inputs. When you make edits in the text field, the changes will be reflected in the Label when you tap Enter, lose focus by clicking the button, switch to a different window, and so on.
import javafx.application.Application;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.converter.IntegerStringConverter;
class IntTextField extends TextField {
private final IntegerProperty value;
TextFormatter<Integer> formatter;
public double getValue() {
return value.getValue();
}
public void setValue(int newValue) {
value.setValue(newValue);
}
public IntegerProperty valueProperty() {
return value;
}
public StringBinding getStringBinding () {
return value.asString();
}
IntTextField(int initValue) {
value = new SimpleIntegerProperty(initValue);
setText(initValue + "");
formatter = new TextFormatter(new IntegerStringConverter(), initValue);
formatter.valueProperty().addListener((ObservableValue<? extends Integer> obs,
Integer oldValue, Integer newValue) -> value.setValue(newValue));
setTextFormatter(formatter);
}
IntTextField() {
this(0);
}
}
public class TFBindingDemo extends Application {
#Override
public void start(Stage stage) {
stage.setTitle("TFBindingDemo");
IntTextField intTextField = new IntTextField(12345);
intTextField.setMaxWidth(150);
Label label = new Label("Type in the TextField");
label.textProperty().bind(intTextField.getStringBinding());
Button removeFocusButton = new Button("Click Here to Remove Focus");
VBox root = new VBox(20, intTextField, label, removeFocusButton);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(20));
Scene scene = new Scene(root, 325, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I'm trying to implement an application, which has a simple navigation. One Main Menu, 3 Submenus, with another 3 Submenus each.
I need the application open every submenu recursively in the same Window with the Mainmenu as the root screen. I must be able to return to that menu by going via the "Back" Button on each Submenu.
I implemented a Main class, a Controller Class and a FXML-file for EACH (!) Menu and Submenu.
E.g. my Main Menu
package application;
import org.apache.log4j.Logger;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
public class Main extends Application {
// Initialize Logger
private static final Logger logger = Logger.getLogger(Main.class);
#Override
public void start(Stage primaryStage)
{
try
{
AnchorPane root = (AnchorPane)FXMLLoader.load(getClass().getResource("MainFrame.fxml"));
Scene scene = new Scene(root,1000,500);
primaryStage.setScene(scene);
primaryStage.show();
}
catch(Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
logger.info("Starting application.");
launch(args);
}
}
My MainController
package application;
import org.apache.log4j.Logger;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class MainFrameController
{
private static final Logger logger = Logger.getLogger(MainFrameController.class);
#FXML
private Button btn_random1;
#FXML
private Button btn_random2;
#FXML
private Button btn_random3;
#FXML
private Button btn_random4;
public void initialize()
{
//mainService = new MainService();
}
#FXML
private void onRandomButton1() throws Exception
{
logger.info("onRandomButton1Clicked");
Stage stage = new Stage();
AnchorPane root;
root = (AnchorPane)FXMLLoader.load(getClass().getResource("RandomFXML1.fxml"));
Scene scene = new Scene(root,1000,500);
stage.setScene(scene);
stage.show();
}
#FXML
private void onRandomButton2()
{
logger.info("onRandomButton1");
}
#FXML
private void onRandomButton3()
{
logger.info("onRandomButton2");
}
#FXML
private void onRandomButton4()
{
Platform.exit();
logger.info("onRandomButton3");
}
}
Is there a way to simply change my code, so it does open in the same window?
I took a look at several tutorials with relatively complex ways of solving this, I'd like to stick to my code and not changing too much, otherwise I'd have to start all over again.
Pls note, that this is only one of many Main/Controller/FXML combinations, I have about 10 screens and "subscreens", which are being navigated like this (by java opening a new window).
Ideas anyone? Or maybe a relatively simple tutorial (for which I dont have to change my whole code)?
Thanks!
Have an empty controller at the root (or perhaps with a single empty anchorpane) and have it open the other controllers and add it to the current pane?
I currently have a similar setup but with a tab pane: each module is loaded into a separate tab. Each module itself has an fxml file, a controller etc. The core code dynamically creates new tabs etc for each module and loads them.