JavaSwing 应用程序中的简单 JavaFX WebView 不显示内容
Simple JavaFX WebView in Java Swing app not displaying contents
下面的代码创建了一个 Java Swing JFrame
,带有一个按钮,可以在对话框中打开 JavaFX WebView
,但是当打开时 Web 视图是空白的而不是显示内容(URL 内容或 "Welcome JavaFX!")。有什么问题吗?
(注:代码基于this and this).
OpenUrlInJFrameAction.java:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URI;
import java.util.Objects;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.web.WebView;
public class OpenUrlInJFrameAction implements ActionListener {
private final JFrame parent;
private final URI uri;
public OpenUrlInJFrameAction(JFrame parent, URI uri) {
this.parent = Objects.requireNonNull(parent);
this.uri = Objects.requireNonNull(uri);
}
@Override
public void actionPerformed(ActionEvent event) {
SwingUtilities.invokeLater(() -> {
// You should execute this part on the Event Dispatch Thread
// because it modifies a Swing component
JDialog jDialog = new JDialog(parent, true);
JFXPanel jfxPanel = new JFXPanel();
jDialog.add(jfxPanel);
jDialog.setSize(800, 600);
jDialog.setLocationRelativeTo(null);
jDialog.setVisible(true);
// Creation of scene and future interactions with JFXPanel
// should take place on the JavaFX Application Thread
Platform.runLater(() -> {
// Uncomment either the lines below Test 1 or below Test 2,
// both are apparently ignored by the web view.
// Test 1
Scene scene = createScene();
jfxPanel.setScene(scene);
// Test 2
/*WebView webView = new WebView();
jfxPanel.setScene(new Scene(webView));
webView.getEngine().load(uri.toString());*/
});
});
}
private Scene createScene() {
Group root = new Group();
Scene scene = new Scene(root, Color.ALICEBLUE);
Text text = new Text();
text.setX(40);
text.setY(100);
text.setFont(new Font(25));
text.setText("Welcome JavaFX!");
root.getChildren().add(text);
return (scene);
}
}
JFrameTest.java:
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class JFrameTest extends JFrame {
public JFrameTest(String title) {
super(Objects.requireNonNull(title));
}
public static void main(String [] args) {
SwingUtilities.invokeLater(() -> {
JFrameTest jFrameTest = new JFrameTest("Test");
jFrameTest.setDefaultCloseOperation(EXIT_ON_CLOSE);
JButton jButton = new JButton("Open dialog");
try {
jButton.addActionListener(new OpenUrlInJFrameAction(jFrameTest,
new URI("https://whosebug.com")));
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
jFrameTest.add(jButton);
jFrameTest.pack();
jFrameTest.setVisible(true);
});
}
}
你的例子提出了几个问题:
您正在创建一个 modal 对话框,默认为 ModalityType.APPLICATION_MODAL
,阻止进一步更新。相反,创建一个 无模式 对话框,如下所示。
dialog = new JDialog(parent, Dialog.ModalityType.MODELESS);
最佳方法取决于应用程序的设计;但是,正如 here 所讨论的,无模式 对话框可能会更加灵活;为避免重复,在初始对话实例上调用 toFront()
,如下所示。
而不是实施 ActionListener
,考虑扩展 AbstractAction
,如下图所示;请注意如何重复使用 Action
。
按钮的 ActionListener
在 event dispatch thread 上触发;重新排队对话框创建没有必要也没有好处。
覆盖 getPreferredSize()
,讨论 here,以建立对话框的初始空大小。
经测试:
import java.awt.BorderLayout;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.web.WebView;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.SwingUtilities;
/**
* @see
*/
public class JFrameTest extends JFrame {
public JFrameTest(String title) {
super(Objects.requireNonNull(title));
}
private static class OpenDialogAction extends AbstractAction {
private final JFrame parent;
private final URI uri;
private JDialog dialog;
public OpenDialogAction(JFrame parent, URI uri) {
super.putValue(NAME, "Open " + uri.getAuthority());
this.parent = Objects.requireNonNull(parent);
this.uri = Objects.requireNonNull(uri);
}
@Override
public void actionPerformed(ActionEvent event) {
if (dialog != null) {
dialog.toFront();
return;
}
dialog = new JDialog(parent, Dialog.ModalityType.MODELESS);
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
dialog = null;
}
});
dialog.setTitle(uri.getAuthority());
JFXPanel fxPanel = new JFXPanel() {
@Override
public Dimension getPreferredSize() {
return new Dimension(640, 480);
}
};
dialog.add(fxPanel);
dialog.pack();
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
Platform.runLater(() -> {
WebView webView = new WebView();
webView.getEngine().load(uri.toString());
Scene scene = new Scene(webView);
fxPanel.setScene(scene);
});
}
}
public static void main(String[] args) throws URISyntaxException {
URI uri1 = new URI("https://www.example.com");
URI uri2 = new URI("https://www.example.net");
URI uri3 = new URI("https://www.example.org");
SwingUtilities.invokeLater(() -> {
JFrameTest test = new JFrameTest("Test");
test.setLayout(new GridLayout(0, 1));
test.add(new JButton(new OpenDialogAction(test, uri1)));
test.add(new JButton(new OpenDialogAction(test, uri2)));
test.add(new JButton(new OpenDialogAction(test, uri3)));
test.pack();
test.setDefaultCloseOperation(EXIT_ON_CLOSE);
test.setLocationByPlatform(true);
test.setVisible(true);
});
}
}
下面的代码创建了一个 Java Swing JFrame
,带有一个按钮,可以在对话框中打开 JavaFX WebView
,但是当打开时 Web 视图是空白的而不是显示内容(URL 内容或 "Welcome JavaFX!")。有什么问题吗?
(注:代码基于this and this).
OpenUrlInJFrameAction.java:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URI;
import java.util.Objects;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.web.WebView;
public class OpenUrlInJFrameAction implements ActionListener {
private final JFrame parent;
private final URI uri;
public OpenUrlInJFrameAction(JFrame parent, URI uri) {
this.parent = Objects.requireNonNull(parent);
this.uri = Objects.requireNonNull(uri);
}
@Override
public void actionPerformed(ActionEvent event) {
SwingUtilities.invokeLater(() -> {
// You should execute this part on the Event Dispatch Thread
// because it modifies a Swing component
JDialog jDialog = new JDialog(parent, true);
JFXPanel jfxPanel = new JFXPanel();
jDialog.add(jfxPanel);
jDialog.setSize(800, 600);
jDialog.setLocationRelativeTo(null);
jDialog.setVisible(true);
// Creation of scene and future interactions with JFXPanel
// should take place on the JavaFX Application Thread
Platform.runLater(() -> {
// Uncomment either the lines below Test 1 or below Test 2,
// both are apparently ignored by the web view.
// Test 1
Scene scene = createScene();
jfxPanel.setScene(scene);
// Test 2
/*WebView webView = new WebView();
jfxPanel.setScene(new Scene(webView));
webView.getEngine().load(uri.toString());*/
});
});
}
private Scene createScene() {
Group root = new Group();
Scene scene = new Scene(root, Color.ALICEBLUE);
Text text = new Text();
text.setX(40);
text.setY(100);
text.setFont(new Font(25));
text.setText("Welcome JavaFX!");
root.getChildren().add(text);
return (scene);
}
}
JFrameTest.java:
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class JFrameTest extends JFrame {
public JFrameTest(String title) {
super(Objects.requireNonNull(title));
}
public static void main(String [] args) {
SwingUtilities.invokeLater(() -> {
JFrameTest jFrameTest = new JFrameTest("Test");
jFrameTest.setDefaultCloseOperation(EXIT_ON_CLOSE);
JButton jButton = new JButton("Open dialog");
try {
jButton.addActionListener(new OpenUrlInJFrameAction(jFrameTest,
new URI("https://whosebug.com")));
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
jFrameTest.add(jButton);
jFrameTest.pack();
jFrameTest.setVisible(true);
});
}
}
你的例子提出了几个问题:
您正在创建一个 modal 对话框,默认为
ModalityType.APPLICATION_MODAL
,阻止进一步更新。相反,创建一个 无模式 对话框,如下所示。dialog = new JDialog(parent, Dialog.ModalityType.MODELESS);
最佳方法取决于应用程序的设计;但是,正如 here 所讨论的,无模式 对话框可能会更加灵活;为避免重复,在初始对话实例上调用
toFront()
,如下所示。而不是实施
ActionListener
,考虑扩展AbstractAction
,如下图所示;请注意如何重复使用Action
。按钮的
ActionListener
在 event dispatch thread 上触发;重新排队对话框创建没有必要也没有好处。覆盖
getPreferredSize()
,讨论 here,以建立对话框的初始空大小。
经测试:
import java.awt.BorderLayout;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.web.WebView;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.SwingUtilities;
/**
* @see
*/
public class JFrameTest extends JFrame {
public JFrameTest(String title) {
super(Objects.requireNonNull(title));
}
private static class OpenDialogAction extends AbstractAction {
private final JFrame parent;
private final URI uri;
private JDialog dialog;
public OpenDialogAction(JFrame parent, URI uri) {
super.putValue(NAME, "Open " + uri.getAuthority());
this.parent = Objects.requireNonNull(parent);
this.uri = Objects.requireNonNull(uri);
}
@Override
public void actionPerformed(ActionEvent event) {
if (dialog != null) {
dialog.toFront();
return;
}
dialog = new JDialog(parent, Dialog.ModalityType.MODELESS);
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
dialog = null;
}
});
dialog.setTitle(uri.getAuthority());
JFXPanel fxPanel = new JFXPanel() {
@Override
public Dimension getPreferredSize() {
return new Dimension(640, 480);
}
};
dialog.add(fxPanel);
dialog.pack();
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
Platform.runLater(() -> {
WebView webView = new WebView();
webView.getEngine().load(uri.toString());
Scene scene = new Scene(webView);
fxPanel.setScene(scene);
});
}
}
public static void main(String[] args) throws URISyntaxException {
URI uri1 = new URI("https://www.example.com");
URI uri2 = new URI("https://www.example.net");
URI uri3 = new URI("https://www.example.org");
SwingUtilities.invokeLater(() -> {
JFrameTest test = new JFrameTest("Test");
test.setLayout(new GridLayout(0, 1));
test.add(new JButton(new OpenDialogAction(test, uri1)));
test.add(new JButton(new OpenDialogAction(test, uri2)));
test.add(new JButton(new OpenDialogAction(test, uri3)));
test.pack();
test.setDefaultCloseOperation(EXIT_ON_CLOSE);
test.setLocationByPlatform(true);
test.setVisible(true);
});
}
}