在一个循环中处理多个 JavaFX 应用程序启动
Handle multiple JavaFX application launches within a loop
我的代码目前通过 IMAP (imaps) 和 javamail 读取我的 Gmail 收件箱,一旦找到带有 zip/xap 附件的电子邮件,它就会显示一个阶段 (window) 询问是否下载文件,是或否。
我希望舞台在我做出选择后关闭,然后 return 到循环中调用的地方。我的问题出现是因为你不能多次启动一个应用程序,所以我读到here我应该在start
方法中写Platform.setImplicitExit(false);
,然后使用primartyStage.hide()
(?)然后当我需要稍后再次显示舞台时 Platform.runLater(() -> primaryStage.show());
之类的东西。
现在出现的问题是命令流从 Mail.java 的 doit()
方法开始,循环遍历我的收件箱,launch(args)
出现在 for loop
中方法内。这意味着 launch(args)
然后调用 start
布景,展示舞台。由于有一个 Controller.java
和 fxml
关联,控制器 class 有一个用于舞台按钮的事件处理程序,一旦 start
显示了舞台,"intercept" 流程.因此,当我单击“是”或“否”时,它会隐藏舞台,但只是 挂在那里。 就好像它不能 return 到启动方法以继续从 launch(参数)发生。我如何在必要时正确 hide/show 阶段,无论单击是还是否都允许循环继续。
这是 Mail.java 和 Controller.java 的代码。非常感谢!
Mail.java
[Other variables set here]
public static int launchCount = 0;@FXML public Text subjectHolder;
public static ReceiveMailImap obj = new ReceiveMailImap();
public static void main(String[] args) throws IOException, MessagingException {
ReceiveMailImap.doit();
}
@Override
public void start(Stage primaryStage) throws Exception {
loader = new FXMLLoader(getClass().getResource("prompts.fxml"));
root = loader.load();
controller = loader.getController();
controller.setPrimaryStage(primaryStage);
scene = new Scene(root, 450, 250);
controller.setPrimaryScene(scene);
scene.getStylesheets().add("styleMain.css");
Platform.setImplicitExit(false);
primaryStage.setTitle("Download this file?");
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void doit() throws MessagingException, IOException {
Folder inbox = null;
Store store = null;
try {
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
store = session.getStore("imaps");
store.connect("imap.gmail.com", "myAccount@gmail.com", "Password");
inbox = store.getFolder("Inbox");
inbox.open(Folder.READ_WRITE);
Message[] messages = inbox.getMessages();
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
fp.add(UIDFolder.FetchProfileItem.FLAGS);
fp.add(UIDFolder.FetchProfileItem.CONTENT_INFO);
fp.add("X-mailer");
inbox.fetch(messages, fp);
int doc = 0;
int maxDocs = 400;
for (int i = messages.length - 1; i >= 0; i--) {
Message message = messages[i];
if (doc < maxDocs) {
doc++;
message.getSubject();
if (!hasAttachments(message)) {
continue;
}
String from = "Sender Unknown";
if (message.getReplyTo().length >= 1) {
from = message.getReplyTo()[0].toString();
} else if (message.getFrom().length >= 1) {
from = message.getFrom()[0].toString();
}
subject = message.getSubject();
if (from.contains("myAccount@gmail.com")) {
saveAttachment(message.getContent());
message.setFlag(Flags.Flag.SEEN, true);
}
}
}
} finally {
if (inbox != null) {
inbox.close(true);
}
if (store != null) {
store.close();
}
}
}
public static boolean hasAttachments(Message msg) throws MessagingException, IOException {
if (msg.isMimeType("multipart/mixed")) {
Multipart mp = (Multipart) msg.getContent();
if (mp.getCount() > 1) return true;
}
return false;
}
public static void saveAttachment(Object content)
throws IOException, MessagingException {
out = null; in = null;
try {
if (content instanceof Multipart) {
Multipart multi = ((Multipart) content);
parts = multi.getCount();
for (int j = 0; j < parts; ++j) {
part = (MimeBodyPart) multi.getBodyPart(j);
if (part.getContent() instanceof Multipart) {
// part-within-a-part, do some recursion...
saveAttachment(part.getContent());
} else {
int allow = 0;
if (part.isMimeType("application/x-silverlight-app")) {
extension = "xap";
allow = 1;
} else {
extension = "zip";
allow = 1;
}
if (allow == 1) {
if (launchCount == 0) {
launch(args);
launchCount++;
} else {
Platform.runLater(() -> primaryStage.show());
}
} else {
continue;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if ( in != null) { in .close();
}
if (out != null) {
out.flush();
out.close();
}
}
}
public static File createFolder(String subject) {
JFileChooser fr = new JFileChooser();
FileSystemView myDocs = fr.getFileSystemView();
String myDocuments = myDocs.getDefaultDirectory().toString();
dir = new File(myDocuments + "\" + subject);
savePathNoExtension = dir.toString();
dir.mkdir();
System.out.println("Just created: " + dir);
return dir;
}
}
Controller.java
public class Controller implements Initializable {
@FXML
private Text subjectHolder;
public Button yesButton, noButton;
public ReceiveMailImap subject;
@Override
public void initialize(URL url, ResourceBundle rb) {
subject= new ReceiveMailImap();
subjectHolder.setText(subject.returnSubject());
}
public Stage primaryStage;
public Scene scene;
@FXML
ComboBox<String> fieldCombo;
public void setPrimaryStage(Stage stage) {
this.primaryStage = stage;
}
public void setPrimaryScene(Scene scene) {
this.scene = scene;
}
public String buttonPressed(ActionEvent e) throws IOException, MessagingException {
Object source = e.getSource();
if(source==yesButton){
System.out.println("How to tell Mail.java that user clicked Yes?");
return "POSITIVE";}
else{subject.dlOrNot("no");
System.out.println("How to tell Mail.java that user clicked No?");
primaryStage.hide();
return "NEGATIVE";}
}
}
您发布的代码存在很多问题,但让我尝试解决您提出的问题。
代码挂起的原因是Application.launch(...)
does not return until the application has exited
总的来说,您在这里误解了 JavaFX 应用程序的整个生命周期。您应该将 start(...)
方法视为等同于 "traditional" Java 应用程序中的 main(...)
方法。唯一需要注意的是start(...)
是在FX Application Thread上执行的,所以如果需要执行任何阻塞代码,需要放在后台线程中。
为方便起见,向 start(...)
方法传递一个 Stage
实例,因为最常见的做法是创建场景图并将其显示在舞台上。不过,您没有义务使用此舞台,您可以忽略它并在需要时创建自己的舞台。
我认为您基本上可以按如下方式构建代码(尽管老实说,我很难理解您在做什么):
public class Mail extends Application {
@Override
public void start(Stage ignored) throws Exception {
Platform.setImplicitExit(false);
Message[] messages = /* retrieve messages */ ;
for (Message message : messages) {
if ( /* need to display window */) {
showMessage(message);
}
}
}
private void showMessage(Message message) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("prompts.fxml"));
Parent root = loader.load();
Controller controller = loader.getController();
Scene scene = new Scene(root, 450, 250);
stage.setScene(scene);
stage.initStyle(StageStyle.UNDECORATED);
stage.setTitle(...);
// showAndWait will block execution until the window is hidden, so
// you can query which button was pressed afterwards:
stage.showAndWait();
if (controller.wasYesPressed()) {
// ...
}
}
// for IDEs that don't support directly launching a JavaFX Application:
public static void main(String[] args) {
launch(args);
}
}
显然,您决定是否显示 window 的逻辑更复杂,但这将为您提供基本结构。
要检查按下了哪个按钮,请按上述方式使用 showAndWait
,然后在您的控制器中执行
public class Controller {
@FXML
private Button yesButton ;
private boolean yesButtonPressed = false ;
public boolean wasYesPressed() {
return yesButtonPressed ;
}
// use different handlers for different buttons:
@FXML
private void yesButtonPressed() {
yesButtonPressed = true ;
closeWindow();
}
@FXML
private void noButtonPressed() {
yesButtonPressed = false ; // not really needed, but makes things clearer
closeWindow();
}
private void closeWindow() {
// can use any @FXML-injected node here:
yesButton.getScene().getWindow().hide();
}
}
我的代码目前通过 IMAP (imaps) 和 javamail 读取我的 Gmail 收件箱,一旦找到带有 zip/xap 附件的电子邮件,它就会显示一个阶段 (window) 询问是否下载文件,是或否。
我希望舞台在我做出选择后关闭,然后 return 到循环中调用的地方。我的问题出现是因为你不能多次启动一个应用程序,所以我读到here我应该在start
方法中写Platform.setImplicitExit(false);
,然后使用primartyStage.hide()
(?)然后当我需要稍后再次显示舞台时 Platform.runLater(() -> primaryStage.show());
之类的东西。
现在出现的问题是命令流从 Mail.java 的 doit()
方法开始,循环遍历我的收件箱,launch(args)
出现在 for loop
中方法内。这意味着 launch(args)
然后调用 start
布景,展示舞台。由于有一个 Controller.java
和 fxml
关联,控制器 class 有一个用于舞台按钮的事件处理程序,一旦 start
显示了舞台,"intercept" 流程.因此,当我单击“是”或“否”时,它会隐藏舞台,但只是 挂在那里。 就好像它不能 return 到启动方法以继续从 launch(参数)发生。我如何在必要时正确 hide/show 阶段,无论单击是还是否都允许循环继续。
这是 Mail.java 和 Controller.java 的代码。非常感谢!
Mail.java
[Other variables set here]
public static int launchCount = 0;@FXML public Text subjectHolder;
public static ReceiveMailImap obj = new ReceiveMailImap();
public static void main(String[] args) throws IOException, MessagingException {
ReceiveMailImap.doit();
}
@Override
public void start(Stage primaryStage) throws Exception {
loader = new FXMLLoader(getClass().getResource("prompts.fxml"));
root = loader.load();
controller = loader.getController();
controller.setPrimaryStage(primaryStage);
scene = new Scene(root, 450, 250);
controller.setPrimaryScene(scene);
scene.getStylesheets().add("styleMain.css");
Platform.setImplicitExit(false);
primaryStage.setTitle("Download this file?");
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void doit() throws MessagingException, IOException {
Folder inbox = null;
Store store = null;
try {
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
store = session.getStore("imaps");
store.connect("imap.gmail.com", "myAccount@gmail.com", "Password");
inbox = store.getFolder("Inbox");
inbox.open(Folder.READ_WRITE);
Message[] messages = inbox.getMessages();
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
fp.add(UIDFolder.FetchProfileItem.FLAGS);
fp.add(UIDFolder.FetchProfileItem.CONTENT_INFO);
fp.add("X-mailer");
inbox.fetch(messages, fp);
int doc = 0;
int maxDocs = 400;
for (int i = messages.length - 1; i >= 0; i--) {
Message message = messages[i];
if (doc < maxDocs) {
doc++;
message.getSubject();
if (!hasAttachments(message)) {
continue;
}
String from = "Sender Unknown";
if (message.getReplyTo().length >= 1) {
from = message.getReplyTo()[0].toString();
} else if (message.getFrom().length >= 1) {
from = message.getFrom()[0].toString();
}
subject = message.getSubject();
if (from.contains("myAccount@gmail.com")) {
saveAttachment(message.getContent());
message.setFlag(Flags.Flag.SEEN, true);
}
}
}
} finally {
if (inbox != null) {
inbox.close(true);
}
if (store != null) {
store.close();
}
}
}
public static boolean hasAttachments(Message msg) throws MessagingException, IOException {
if (msg.isMimeType("multipart/mixed")) {
Multipart mp = (Multipart) msg.getContent();
if (mp.getCount() > 1) return true;
}
return false;
}
public static void saveAttachment(Object content)
throws IOException, MessagingException {
out = null; in = null;
try {
if (content instanceof Multipart) {
Multipart multi = ((Multipart) content);
parts = multi.getCount();
for (int j = 0; j < parts; ++j) {
part = (MimeBodyPart) multi.getBodyPart(j);
if (part.getContent() instanceof Multipart) {
// part-within-a-part, do some recursion...
saveAttachment(part.getContent());
} else {
int allow = 0;
if (part.isMimeType("application/x-silverlight-app")) {
extension = "xap";
allow = 1;
} else {
extension = "zip";
allow = 1;
}
if (allow == 1) {
if (launchCount == 0) {
launch(args);
launchCount++;
} else {
Platform.runLater(() -> primaryStage.show());
}
} else {
continue;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if ( in != null) { in .close();
}
if (out != null) {
out.flush();
out.close();
}
}
}
public static File createFolder(String subject) {
JFileChooser fr = new JFileChooser();
FileSystemView myDocs = fr.getFileSystemView();
String myDocuments = myDocs.getDefaultDirectory().toString();
dir = new File(myDocuments + "\" + subject);
savePathNoExtension = dir.toString();
dir.mkdir();
System.out.println("Just created: " + dir);
return dir;
}
}
Controller.java
public class Controller implements Initializable {
@FXML
private Text subjectHolder;
public Button yesButton, noButton;
public ReceiveMailImap subject;
@Override
public void initialize(URL url, ResourceBundle rb) {
subject= new ReceiveMailImap();
subjectHolder.setText(subject.returnSubject());
}
public Stage primaryStage;
public Scene scene;
@FXML
ComboBox<String> fieldCombo;
public void setPrimaryStage(Stage stage) {
this.primaryStage = stage;
}
public void setPrimaryScene(Scene scene) {
this.scene = scene;
}
public String buttonPressed(ActionEvent e) throws IOException, MessagingException {
Object source = e.getSource();
if(source==yesButton){
System.out.println("How to tell Mail.java that user clicked Yes?");
return "POSITIVE";}
else{subject.dlOrNot("no");
System.out.println("How to tell Mail.java that user clicked No?");
primaryStage.hide();
return "NEGATIVE";}
}
}
您发布的代码存在很多问题,但让我尝试解决您提出的问题。
代码挂起的原因是Application.launch(...)
does not return until the application has exited
总的来说,您在这里误解了 JavaFX 应用程序的整个生命周期。您应该将 start(...)
方法视为等同于 "traditional" Java 应用程序中的 main(...)
方法。唯一需要注意的是start(...)
是在FX Application Thread上执行的,所以如果需要执行任何阻塞代码,需要放在后台线程中。
为方便起见,向 start(...)
方法传递一个 Stage
实例,因为最常见的做法是创建场景图并将其显示在舞台上。不过,您没有义务使用此舞台,您可以忽略它并在需要时创建自己的舞台。
我认为您基本上可以按如下方式构建代码(尽管老实说,我很难理解您在做什么):
public class Mail extends Application {
@Override
public void start(Stage ignored) throws Exception {
Platform.setImplicitExit(false);
Message[] messages = /* retrieve messages */ ;
for (Message message : messages) {
if ( /* need to display window */) {
showMessage(message);
}
}
}
private void showMessage(Message message) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("prompts.fxml"));
Parent root = loader.load();
Controller controller = loader.getController();
Scene scene = new Scene(root, 450, 250);
stage.setScene(scene);
stage.initStyle(StageStyle.UNDECORATED);
stage.setTitle(...);
// showAndWait will block execution until the window is hidden, so
// you can query which button was pressed afterwards:
stage.showAndWait();
if (controller.wasYesPressed()) {
// ...
}
}
// for IDEs that don't support directly launching a JavaFX Application:
public static void main(String[] args) {
launch(args);
}
}
显然,您决定是否显示 window 的逻辑更复杂,但这将为您提供基本结构。
要检查按下了哪个按钮,请按上述方式使用 showAndWait
,然后在您的控制器中执行
public class Controller {
@FXML
private Button yesButton ;
private boolean yesButtonPressed = false ;
public boolean wasYesPressed() {
return yesButtonPressed ;
}
// use different handlers for different buttons:
@FXML
private void yesButtonPressed() {
yesButtonPressed = true ;
closeWindow();
}
@FXML
private void noButtonPressed() {
yesButtonPressed = false ; // not really needed, but makes things clearer
closeWindow();
}
private void closeWindow() {
// can use any @FXML-injected node here:
yesButton.getScene().getWindow().hide();
}
}