问题
I am working on a project which deals with multiple fxml and corresponding controller files. I need to somehow get access to an fxml element defined in say A.fxml from the controller of B.fxml and use it.
I am not allowed to show the actual code. However, for this purpose, I have built a simple application with two FXMLs and their corresponding controllers.
This application has Button.fxml with ButtonController.java and ProgressIndicator.fxml with ProgressIndicatorController.java
Upon clicking the button, the Progress indicator would come live.
Below is the code :
Button.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.Pane?>
<AnchorPane xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.ButtonController">
<children>
<Pane prefHeight="206.0" prefWidth="281.0">
<children>
<Button fx:id="button1" layoutX="59.0" layoutY="63.0" mnemonicParsing="false" prefHeight="63.0" prefWidth="164.0" text="Button" onAction="#onButton1Clicked" />
</children>
</Pane>
</children>
</AnchorPane>
ButtonController.java
package application;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
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.ProgressIndicator;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ButtonController implements Initializable{
@FXML
private Button button1;
private ProgressIndicator progressIndicator;
private ProgressIndicatorController progressIndicatorController;
@Override
public void initialize(URL location, ResourceBundle resources) {
try {
Object loader = FXMLLoader.load(getClass().getResource("ProgressIndicator.fxml"));
Scene sceneProgressIndicator = new Scene((Parent) loader, 1400, 800);
sceneProgressIndicator.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
sceneProgressIndicator.setFill(Color.TRANSPARENT);
Stage primaryStageProgressIndicator = new Stage();
primaryStageProgressIndicator.setScene(sceneProgressIndicator);
primaryStageProgressIndicator.setResizable(false);
primaryStageProgressIndicator.setHeight(311);
primaryStageProgressIndicator.setWidth(523);
primaryStageProgressIndicator.show();
} catch (IOException e) {
e.printStackTrace();
}
// TODO Auto-generated method stub
}
public void onButton1Clicked() {
FXMLLoader fxmlLoaderProgressIndicator = new FXMLLoader();
fxmlLoaderProgressIndicator.setLocation(getClass().getResource("ProgressIndicator.fxml"));
try {
Parent root = fxmlLoaderProgressIndicator.load();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
progressIndicatorController = (ProgressIndicatorController) fxmlLoaderProgressIndicator.getController();
for(double i = 0.0; i <= 1.0 ; i+=0.2) {
progressIndicator = progressIndicatorController.getProgressIndicator1();
progressIndicator.setProgress(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
ProgressIndicator.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ProgressIndicator?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.Pane?>
<AnchorPane xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.ProgressIndicatorController">
<children>
<Pane prefHeight="200.0" prefWidth="200.0">
<children>
<ProgressIndicator fx:id="progressIndicator1" layoutX="47.0" layoutY="31.0" prefHeight="103.0" prefWidth="106.0" progress="0.0" />
</children>
</Pane>
</children>
</AnchorPane>
ProgressindicatorController.java
package application;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ProgressIndicatorController implements Initializable {
@FXML
private ProgressIndicator progressIndicator1;
@Override
public void initialize(URL location, ResourceBundle resources) {
// TODO Auto-generated method stub
}
public ProgressIndicator getProgressIndicator1() {
System.out.println("getteeerrrrrrrr");
return progressIndicator1;
}
public void setProgressIndicator1(ProgressIndicator progressIndicator1) {
this.progressIndicator1 = progressIndicator1;
}
}
The getter method is successfully called. However when I click the button, the progress indicator doesnot get updated.
I am also not getting any kind of error messages .
I came across this link Javafx - how to acces FXML "objects" which indeed helped me to a great extent,but I'm not able to get this work too. I just tried to modify the button click method as follows :
onButton1Cliked()
public void onButton1Clicked() {
progressIndicator = (ProgressIndicator) sceneProgressIndicator.lookup("#progressIndicator1");
for(double i = 0.0; i <= 1.0 ; i+=0.2) {
try {
System.out.println("Progressingggg " + i);
progressIndicator.setProgress(i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Clicking on the button does indeed print out the statements. But the progress indicator doesn't get updated with each for loop iteration. Its only at last that the progress indicator gets updated and shows done.
Any idea why this is happening ?
Also is there any other way to access the fxml elements other than using a lookup method. Can my previous approach(trying to get the fxml elements through controller) be made to work somehow.
Any advice or suggestions are most welcome. Been into this for several hours. Stackoverflow seems to be the last resort.
EDIT 1 : START
It turned out that the progress Indicator was not updating because of the reason explained in Zephyr's first comment. So i created a separte thread for my application and that seemed to work fine as the progress indicator got updated as expected.
Working version of onButton1Clicked() method that is successfully updating Progress Indicator :
public void onButton1Clicked() {
progressIndicator = (ProgressIndicator) sceneProgressIndicator.lookup("#progressIndicator1");
Runnable runnable = new Runnable() {
@Override
public void run() {
for(double i = 0.0; i <= 1.0 ; i+=0.2) {
try {
System.out.println("Progressingggg " + i);
progressIndicator.setProgress(i);
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
Having resolved this threading issue, I tried once again to go by the original way mentioned in my OP(i.e. to get the ProgressIndicatorController instance and access its progress indicator and then try updating it). However, this is not working for me. I tried debugging and found that I am getting back the ProgressIndicatorController and ProgressIndicator instance back.
Why then my code is unable to update the progress indicator if it has a hold on the progress indicator instance. The println methods are executed successfully.Its just the progress indicator not getting updated at all.It stands at "0" all the time.
Version of onButton1Clicked() method that is NOT updating progressIndicator :
public void onButton1Clicked() {
FXMLLoader fxmlLoaderProgressIndicator = new FXMLLoader();
fxmlLoaderProgressIndicator.setLocation(getClass().getResource("ProgressIndicator.fxml"));
try {
Parent root = fxmlLoaderProgressIndicator.load();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
progressIndicatorController = (ProgressIndicatorController) fxmlLoaderProgressIndicator.getController();
progressIndicator = progressIndicatorController.getProgressIndicator1();
Runnable runnable = new Runnable() {
@Override
public void run() {
for(double i = 0.0; i <= 1.0 ; i+=0.2) {
try {
System.out.println("Progressingggg " + i);
progressIndicator.setProgress(i);
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
EDIT 1 : END
EDIT 2 : START
Adding other files required to run this sample application
Main.java
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
Object root =FXMLLoader.load(getClass().getResource("Button.fxml"));
Scene scene = new Scene((Parent) root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
My application.css file has nothing in it. Its empty.
I'm using JavaFX8 on Java 8. IDE is Eclipse Photon
I created my project using e(fx)clipse plugin and choosing JavaFX Project from create new project window.
EDIT 2 : END
EDIT 3 : START
Under the section EDIT 1 while trying to test the version of onButton1Clicked() that successfully updates the progress indicator please don't forget to include private Scene sceneProgressIndicator
as the member variable of the class ButtonController.java.
EDIT 3 : END
回答1:
I assume you wanted something like this (note the comments):
import java.io.IOException;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ButtonController {
@FXML
private Button button1;
private ProgressIndicator progressIndicator;
private Scene sceneProgressIndicator;
@FXML
public void initialize() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("ProgressIndicator.fxml"));
Pane progressIndicatorPane = loader.load();
ProgressIndicatorController progressIndicatorController = loader.getController(); //get a reference to the progress indicator controller
progressIndicator = progressIndicatorController.getProgressIndicator1(); //get the progress indicator
sceneProgressIndicator = new Scene(progressIndicatorPane, 1400, 800);
sceneProgressIndicator.setFill(Color.TRANSPARENT);
Stage primaryStageProgressIndicator = new Stage();
primaryStageProgressIndicator.setScene(sceneProgressIndicator);
primaryStageProgressIndicator.setResizable(false);
primaryStageProgressIndicator.setHeight(311);
primaryStageProgressIndicator.setWidth(523);
primaryStageProgressIndicator.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public void onButton1Clicked() {
Runnable runnable = () -> {
for(double i = 0.0; i <= 1.0 ; i+=0.2) {
final double fD = i;
Platform.runLater(() -> progressIndicator.setProgress(fD));//update done on javafx thread
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
}
来源:https://stackoverflow.com/questions/56497485/unable-to-control-elements-of-another-fxml-file-through-another-controller