Using getHostServices() without reference to Application [duplicate] - javafx

Is there any easy way to put into the toHomepage() method the getHostServices().showDocument() command somehow, instead of doing lines and lines of code, so the code should look clean and simple?
package sample;
import javafx.application.HostServices;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
public class Controller {
#FXML
private Button facebookButton;
#FXML
void toHomepage(ActionEvent event) {
}
}
If I press the button, it should directly link me to the Facebook URL

You need to pass the HostServices to the Controller.
Key Code: Set the HostServices in the Controller.
HostServices hostServices ;
public void setGetHostController(HostServices hostServices)
{
this.hostServices = hostServices;
}
Key Code: Passing HostServices to the Controller.
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
Parent root = loader.load();
FXMLDocumentController fXMLDocumentController = loader.getController();
fXMLDocumentController.setGetHostController(getHostServices());
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author sedrick
*/
public class JavaFXApplication7 extends Application {
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
Parent root = loader.load();
FXMLDocumentController fXMLDocumentController = loader.getController();
fXMLDocumentController.setGetHostController(getHostServices());
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Controller
import java.net.URL;
import java.util.ResourceBundle;
import javafx.application.HostServices;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
/**
*
* #author sedri
*/
public class FXMLDocumentController implements Initializable {
HostServices hostServices;
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
hostServices.showDocument("www.google.com");
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
public void setGetHostController(HostServices hostServices)
{
this.hostServices = hostServices;
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication7.FXMLDocumentController">
<children>
<Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
<Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
</children>
</AnchorPane>

Related

Share the same reusable FXML component in multiple views

I have a FXML view that contains a TabView with multiple tabs.
Every Tab has it's own FXML component + controller.
I would like all tabs to report progress via a component defined defined in the main view.
I know that I can inject child controllers into the main controller via FXML.
But to my current understanding, I need to access the parent controller inside my child controllers since the component is located in the main view.
I can also access the child controllers inside the initialize method of my main view. If I follow this path, I would need to modify the child controllers to set the shared component.
This is suboptimal, since the child components would be dependent on a main view that is performing those changes.
I created a MWE to illustrate the dilemma.
The question in short:
How can I report progress of Service1 and Service2 to the progressAndStatusGrid in the main view?
Callenge in short:
Make this application not throw a NPE and report progress to the progress component ;)
MWE:
Launcher:
package org.example;
public class Launcher {
public static void main(final String[] args) {
SimpleApplication.main(args);
}
}
Application:
package org.example;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class SimpleApplication extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/MainView.fxml"));
#SuppressWarnings("unused")
MainViewController controller = fxmlLoader.getController();
final Parent root = fxmlLoader.load();
final Scene scene = new Scene(root);
primaryStage.setTitle("Hello World");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
MainController:
package org.example;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.GridPane;
import java.net.URL;
import java.util.ResourceBundle;
public class MainViewController implements Initializable {
#FXML
GridPane progressAndStatusGrid;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
}
}
MainView:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane prefHeight="150.0" prefWidth="350.0" xmlns="http://javafx.com/javafx/17.0.2-ea"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.MainViewController">
<padding>
<Insets top="4" right="4" bottom="4" left="4"/>
</padding>
<top>
</top>
<center>
<TabPane>
<Tab text="tab1" closable="false">
<fx:include fx:id="tab1" source="Tab1View.fxml"/>
</Tab>
<Tab text="tab2" closable="false">
<fx:include fx:id="tab2" source="Tab2View.fxml"/>
</Tab>
</TabPane>
</center>
<bottom>
<fx:include fx:id="progressAndStatusGrid"
source="ProgressAndStatusGridComponent.fxml"/>
</bottom>
</BorderPane>
"Shared" componentController:
package org.example;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.HBox;
import java.net.URL;
import java.util.ResourceBundle;
public class ProgressAndStatusGridComponentController implements Initializable {
#FXML
ProgressBar progressBar;
#FXML
HBox progressStatusBox;
#FXML
Label progressLabel;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
}
}
"Shared" componentView:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.layout.*?>
<GridPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.example.ProgressAndStatusGridComponentController"
hgap="4" vgap="4">
<padding>
<Insets top="4" right="4" bottom="4" left="4"/>
</padding>
<fx:define>
<ColumnConstraints fx:id="colConstraints2" percentWidth="100"/>
</fx:define>
<columnConstraints>
<fx:reference source="colConstraints2"/>
<fx:reference source="colConstraints2"/>
</columnConstraints>
<ProgressBar fx:id="progressBar" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
<HBox fx:id="progressStatusBox" alignment="CENTER" spacing="4" GridPane.columnIndex="1" GridPane.rowIndex="0">
<padding>
<Insets top="4" right="4" bottom="4" left="4"/>
</padding>
<Label fx:id="progressLabel"/>
</HBox>
</GridPane>
Tab1Controller:
package org.example;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import java.net.URL;
import java.util.ResourceBundle;
public class Tab1Controller implements Initializable {
#FXML
Button button1;
Service1 service1 = new Service1();
// How to get a reference, that is already initialized?
#FXML
ProgressAndStatusGridComponentController progressAndStatusGridComponentController;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
button1.disableProperty().bind(service1.runningProperty());
}
public void handleButtonClick(ActionEvent actionEvent) {
service1.cancel();
service1.reset();
progressAndStatusGridComponentController.progressBar.progressProperty().bind(service1.progressProperty());
progressAndStatusGridComponentController.progressLabel.textProperty().bind(service1.messageProperty());
service1.start();
}
}
Tab1View:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.example.Tab1Controller">
<center>
<Button fx:id="button1" text="Start Background Progress #1" onAction="#handleButtonClick"/>
</center>
</BorderPane>
Tab2Controller:
package org.example;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import java.net.URL;
import java.util.ResourceBundle;
public class Tab2Controller implements Initializable {
#FXML
Button button2;
Service2 service2 = new Service2();
// How to get a reference, that is already initialized?
#FXML
ProgressAndStatusGridComponentController progressAndStatusGridComponentController;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
button2.disableProperty().bind(service2.runningProperty());
}
public void handleButtonClick(ActionEvent actionEvent) {
service2.cancel();
service2.reset();
progressAndStatusGridComponentController.progressBar.progressProperty().bind(service2.progressProperty());
progressAndStatusGridComponentController.progressLabel.textProperty().bind(service2.messageProperty());
service2.start();
}
}
Tab2View:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.example.Tab2Controller">
<center>
<Button fx:id="button2" text="Start Background Progress #2" onAction="#handleButtonClick"/>
</center>
</BorderPane>
Service1:
package org.example;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
public class Service1 extends Service<Void> {
static final int workLoad = 100;
#Override
protected Task<Void> createTask() {
return new Task<>() {
#Override
protected Void call() throws Exception {
updateMessage("Starting Task #1..");
for (int i = 0; i < workLoad; i++) {
Thread.sleep(200);
updateProgress(i, workLoad);
updateMessage(i + " elements done");
}
updateMessage("Task #1 done!");
return null;
}
};
}
}
Service2:
package org.example;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
public class Service2 extends Service<Void> {
static final int workLoad = 100;
#Override
protected Task<Void> createTask() {
return new Task<>() {
#Override
protected Void call() throws Exception {
updateMessage("Starting Task #2..");
for (int i = 0; i < workLoad; i++) {
Thread.sleep(200);
updateProgress(i, workLoad);
updateMessage(i + " elements done");
}
updateMessage("Task #2 done!");
return null;
}
};
}
}

JavaFX Custom control does not appear in Scene

I started to learn JavaFX for couple of days. I took a lot of stackoverflow examples, google-it, youtube videos, and all seems good in examples, but mine does not work and I don't know what I'm doing wrong.
So, I make tests only for now, nothing important, it is not an application in real world.
I've built a Custom Control using SceneBuilder, so this is the fxml file for it:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane fx:id="cControl" prefHeight="100.0" prefWidth="100.0"
style="-fx-border-color: #111111; -fx-border-radius: 2; -fx-background-color: linear-gradient(from 50% 0% to 50% 100%, #f0f0f0, orangered); -fx-background-radius: 2;"
xmlns="http://javafx.com/javafx/19"
xmlns:fx="http://javafx.com/fxml/1"
>
<children>
<Button fx:id="btnClickMe" layoutX="12.0" layoutY="67.0" mnemonicParsing="false" onAction="#onClickMeAction" prefHeight="25.0" prefWidth="77.0" text="Button" AnchorPane.bottomAnchor="8.0" />
<Label fx:id="lblTitle" alignment="CENTER" contentDisplay="CENTER" focusTraversable="false" layoutY="1.0" prefHeight="43.0" prefWidth="100.0" text="Label" textAlignment="CENTER" AnchorPane.bottomAnchor="56.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="1.0" />
</children>
</AnchorPane>
The model is this:
PackTestModel.java
package com.myapp.customcontrols;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class PackTestModel {
public PackTestModel(String labelText) {
setLabelText(labelText);
}
public String getLabelText() {
return labelText.get();
}
public StringProperty labelTextProperty() {
return labelText;
}
public void setLabelText(String labelText) {
this.labelText.set(labelText);
}
private final StringProperty labelText = new SimpleStringProperty();
}
This is the controller:
package com.myapp.customcontrols;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import java.net.URL;
import java.util.ResourceBundle;
public class PackTestController implements Initializable {
#FXML
private Button btnClickMe;
#FXML
private Label lblTitle;
private final PackTestModel packTestModel;
public PackTestController(PackTestModel ptm) {
packTestModel = ptm;
}
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
lblTitle.textProperty().bind(packTestModel.labelTextProperty());
}
}
This is the code for my CustomControl class:
package com.myapp.customcontrols;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.layout.AnchorPane;
import java.io.IOException;
public class PackTestCC extends AnchorPane {
private PackTestController packTestController;
public PackTestCC(PackTestModel model) throws IOException {
super();
FXMLLoader loader = new FXMLLoader(getClass().getResource("PackTest.fxml"));
packTestController = new PackTestController(model);
loader.setController(packTestController);
try {
Node n = loader.load();
this.getChildren().add(n);
}catch (RuntimeException e){}
}
}
And my main application Main.java file looks like this:
package com.myapp;
import com.myapp.customcontrols.PackTestCC;
import com.myapp.customcontrols.PackTestModel;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URISyntaxException;
public class Main extends Application {
private MainController2 mainController;
#Override
public void start(Stage stage) throws IOException, URISyntaxException {
Font.loadFont(getClass().getResourceAsStream("Fonts/DINOTBold.otf"), 16);
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("main-view2.fxml"));
mainController = new MainController2();
fxmlLoader.setController(mainController);
Parent n = fxmlLoader.load();
Scene scene = new Scene(n, 800, 600);
mainController.getAnchorPane().getChildren().add(new PackTestCC(new PackTestModel("This is for test")));
stage.setScene(scene);
stage.setMinWidth(800);
stage.setMinHeight(600);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
main-vew2.fxml is very simple, as long as it is for tests:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane
fx:id="anchorPane" prefHeight="300.0" prefWidth="300.0"
style="-fx-border-color: #111111; -fx-border-insets: 5;"
xmlns="http://javafx.com/javafx/19"
xmlns:fx="http://javafx.com/fxml/1" />
and this is MainController2 class:
package com.myapp;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.AnchorPane;
import java.net.URL;
import java.util.ResourceBundle;
public class MainController2 implements Initializable {
#FXML
private AnchorPane anchorPane;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
}
public AnchorPane getAnchorPane() {
return anchorPane;
}
}
The Custom Control must look like this, according to SceneBuilder UI:
but, when I run my application, all I see is only the border from main-view2.fxml:
What I'm doing wrong, what I'm missing here?

Change Label text during runtime in javafx

I load the FXML1 in the start method and when the Button is clicked i open up the FXML2. Here's my question: can I change the text of the Label "label" when the button is pressed?
Main:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class FXMLExample extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXML1.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Controller:
in the initialize method is the comment: label.setText("Hello World");
If I uncomment it and try it i get an Exception.
package fxmlexample;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Stage;
/**
* FXML Controller class
*/
public class FXMLController implements Initializable {
#FXML
Button button;
#FXML
Label label;
public void pressButton(ActionEvent event) throws Exception {
if (event.getSource() == button) {
Parent root = FXMLLoader.load(getClass().getResource("FXML2.fxml"));
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
}
}
/**
* Initializes the controller class.
*
* #param url
* #param rb
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// label.setText("Hello World");
}
}
FXML1:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.Pane?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fxmlexample.FXMLController">
<children>
<Button fx:id="button" layoutX="264.0" layoutY="185.0" mnemonicParsing="false" onAction="#pressButton" prefHeight="31.0" prefWidth="73.0" text="Click me!" />
</children>
</Pane>
FXML2:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<Pane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fxmlexample.FXMLController">
<children>
<Label fx:id="label" layoutX="244.0" layoutY="177.0" prefHeight="46.0" prefWidth="112.0" style="-fx-background-color: lime;" />
</children>
</Pane>
The label is only initialized in the controller for FXML2 (because that FXML file has an element with fx:id="label"). The initialize() method is called on both the controllers. Consequently, when initialize() is called on the controller for FXML1, label is null and you get a null pointer exception.
Use a different controller class for each of the FXML files:
package fxmlexample;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
/**
* FXML Controller class
*/
public class FXMLController1 implements Initializable {
#FXML
Button button;
public void pressButton(ActionEvent event) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXML2.fxml"));
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
}
/**
* Initializes the controller class.
*
* #param url
* #param rb
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
}
}
and
package fxmlexample;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
/**
* FXML Controller class
*/
public class FXMLController2 implements Initializable {
#FXML
Label label;
/**
* Initializes the controller class.
*
* #param url
* #param rb
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
label.setText("Hello World");
}
}
Finally, modify the fx:controller attribute in each FXML file to point to the correct class.

Using ImageView in JavaFX controller class

I created a javaFX fxml application in NetBeans and want to display an image using a filechooser opened in a handleButtonAction event.
If I drag an ImageView into the panel using the gui builder I see no way to get it's object generated in code.
If I add an Imageview manually to the main class, I do not have the button handler method available, and if I add it to the controller class, I do not have the main panel available to attach the Imageview to. I think I am not understanding how to elements from the ui builder generated into code.
The generated NetBeans projects starts with a hello button that also has no visible object in the source files, so it seems clear that I am missing something about how the ui builder sets the proper xml data to have those elements available to the controller, but there doesn't appear to me to be any way add these to the controller from the ui builder. Is it just that the UI builder is unreliable?
Any help would be appreciated.
The main application class looks like:
package javafxapplication2;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class JavaFXApplication2 extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
And the Controller:
package javafxapplication2;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
//import javafx.scene.image.Image;
//import javafx.scene.image.ImageView;
//import javafx.stage.FileChooser;
//import javax.imageio.ImageIO;
//import java.io.File;
//import java.io.IOException;
public class FXMLDocumentController implements Initializable {
// private ImageView imgview = new ImageView();
// private FileChooser filechooser = new FileChooser();
#FXML
private void handleButtonAction(ActionEvent event) {
//commented out because it displays nothing
// try {
// File infile = filechooser.showOpenDialog(null);
// Image img = SwingFXUtils.toFXImage(ImageIO.read(infile), null);
// imgview.setImage(img);
// } catch (IOException ex) {
// Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
// }
System.out.println("button clicked");
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
And the XML:
<?import javafx.scene.image.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="355.0" prefWidth="411.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication2.FXMLDocumentController">
<children>
<Button fx:id="button" layoutX="14.0" layoutY="312.0" onAction="#handleButtonAction" text="open file" />
<ImageView fitHeight="150.0" fitWidth="200.0" layoutX="84.0" layoutY="73.0" pickOnBounds="true" preserveRatio="true" />
<Button layoutX="212.0" layoutY="310.0" mnemonicParsing="false" text="Button" />
</children>
</AnchorPane>
You aren't using the ImageView that you have created in the FXML, inside your controller.
You should assign the ImageView a fx:id and then inject it in the controller. This way the same ImageView will be used while setting the Image in the ImageView.
In FXML:
<ImageView fx:id="imgview" fitHeight="150.0" fitWidth="200.0"
layoutX="84.0" layoutY="73.0" pickOnBounds="true" preserveRatio="true" />
In Controller:
public class FXMLDocumentController implements Initializable {
#FXML
private ImageView imgview;
...
}
MCVE
FXML
<?import javafx.scene.control.Button?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="355.0" prefWidth="411.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="FXMLDocumentController">
<children>
<Button fx:id="button" layoutX="14.0" layoutY="312.0" onAction="#handleButtonAction" text="open file" />
<ImageView fx:id="imageView" fitHeight="150.0" fitWidth="200.0" layoutX="84.0" layoutY="73.0" pickOnBounds="true" preserveRatio="true" />
<Button layoutX="212.0" layoutY="310.0" mnemonicParsing="false" text="Button" />
</children>
</AnchorPane>
Controller
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.FileChooser;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
public class FXMLDocumentController implements Initializable {
#FXML
private ImageView imageView;
private FileChooser filechooser = new FileChooser();
#FXML
private void handleButtonAction(ActionEvent event) {
try {
File infile = filechooser.showOpenDialog(null);
Image img = SwingFXUtils.toFXImage(ImageIO.read(infile), null);
imageView.setImage(img);
} catch (IOException ex) {
Logger.getLogger(TestController.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
Main
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 stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX: Button click in one FXML affects behaviour in another FXML

I am a newbie in JavaFX so I decided to make a simple web browser and it works fine but only with a predefined URL (http://google.com in this example). I did the following: created BrowserTop.fxml (which includes a TextField for URL and a button for getting this URL) and Browser.fxml which includes plain WebView. So, my problem is that I can't understand how can I pass a String URL to WebView (which uses Browser.fxml) by clicking the button in BrowserTop.fxml?
Browser.java
package browser;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class Browser extends Application {
private Stage stage;
private BorderPane rootLayout;
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
initBrowserTop();
initBrowserMain();
}
public void initBrowserTop() throws IOException{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Browser.class.getResource("BrowserTop.fxml"));
rootLayout = (BorderPane) loader.load();
Scene scene = new Scene(rootLayout);
stage.setScene(scene);
stage.show();
}
public void initBrowserMain() throws IOException{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Browser.class.getResource("Browser.fxml"));
WebView browserWebView = (WebView) loader.load();
WebEngine myWebEngine = browserWebView.getEngine();
myWebEngine.load("http://google.com");
rootLayout.setCenter(browserWebView);
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
BrowserTop.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHe
ight="728.0" prefWidth="1024.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="browser.BrowserTopController">
<top>
<AnchorPane>
<children>
<TextField fx:id="txtURL" layoutX="52.0" prefHeight="25.0" prefWidth="584.0" AnchorPane.leftAnchor="52.0" AnchorPane.rightAnchor="0.0" BorderPane.alignment="CENTER" />
<Button fx:id="btnGo" mnemonicParsing="false" onAction="#handleButtonAction" prefHeight="25.0" prefWidth="53.0" text="Go" AnchorPane.leftAnchor="0.0" />
</children>
</AnchorPane>
</top>
</BorderPane>
BrowserTopController.java
package browser;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
public class BrowserTopController implements Initializable {
#FXML TextField txtURL;
#FXML
public void handleButtonAction(ActionEvent event){
//something should be here
}
/**
* Initializes the controller class.
* #param url
* #param rb
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
I have actually used the techniques referred above by #James_D before. I made your code work by doing this:
package browser;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class Browser extends Application {
private Stage stage;
private BorderPane rootLayout;
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Browser.class.getResource("BrowserTop.fxml"));
rootLayout = (BorderPane) loader.load();
BrowserTopController browserTopController = (BrowserTopController) loader.getController();
WebView browserWebView = new WebView();
WebEngine myWebEngine = browserWebView.getEngine();
browserTopController.btnGo.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
myWebEngine.load(browserTopController.txtURL.getText());
}
});
rootLayout.setCenter(browserWebView);
Scene scene = new Scene(rootLayout);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
You also have to add
#FXML Button btnGo;
to your fxml document.

Resources