我正在从事一项需要从html内容调用java方法的任务。这是一个摇摆应用程序,我使用JavaFX WebView将HTML内容加载到该应用程序中。但是,当我尝试调用Java方法时,它无法正常工作,有时会出现致命错误并导致应用程序崩溃。
Java类
class Solution extends JFrame {
private JFXPanel jfxPanel;
static JFrame f;
public static void main(String[] args) {
new Solution().createUI();
}
private void createUI() {
f = new JFrame("panel");
JPanel p = new JPanel();
jfxPanel = new JFXPanel();
createScene();
p.add(jfxPanel);
f.add(p);
f.setSize(300, 300);
f.show();
}
private void createScene() {
PlatformImpl.setImplicitExit(false);
PlatformImpl.runAndWait(new Runnable() {
@Override
public void run() {
BorderPane borderPane = new BorderPane();
WebView webComponent = new WebView();
WebEngine webEngine = webComponent.getEngine();
webEngine.load(TestOnClick.class.getResource("/mypage.html").toString());
borderPane.setCenter(webComponent);
Scene scene = new Scene(borderPane,300,300);
jfxPanel.setScene(scene);
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", new Solution());
}
});
}
public void onClick() {
System.out.println("Invoked from JS");
}
}
HTML
<button onclick="app.onClick()">Click ME</button>
请让我知道这里需要更改的内容
答案 0 :(得分:1)
在documentation中,用于回调的类和方法都必须为public
:
从JavaScript回调到Java
JSObject.setMember方法可用于启用来自 将JavaScript转换为Java代码,如以下示例所示。 Java代码建立了一个名为app的新JavaScript对象。这个 对象具有一个公共成员,方法退出。
public class JavaApplication { public void exit() { Platform.exit(); } } ... JavaApplication javaApp = new JavaApplication(); JSObject window = (JSObject) webEngine.executeScript("window"); window.setMember("app", javaApp);
...
必须同时将Java类和方法声明为公共。
(我的重点。)
您的Solution
类不是公开的,因此它将无法正常工作。
此外,在加载新文档时,window
将失去其属性。由于加载是异步发生的,因此您需要确保在文档加载后在窗口上设置成员。您可以通过documentProperty()
上的侦听器来完成此操作:
webEngine.documentProperty().addListener((obs, oldDoc, newDoc) -> {
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", this);
});
webEngine.load(Solution.class.getResource("/mypage.html").toString());
您的代码还有很多其他问题:
JFrame
必须在AWT事件分配线程上构造(相同的规则也适用于修改JFrame
中显示的组件)。您可以通过将调用包装到createUI()
中的SwingUtilities.invokeLater(...)
来实现。Solution
设为JFrame
的子类,以及为什么 在JFrame
中创建新的createUI()
。由于您从不使用Solution
继承JFrame
子类的事实,因此应将其删除。PlatformImpl
不是公共API的一部分:因此,JavaFX团队在以后的版本中删除该类是完全可以的。您应该使用Platform
类中的方法。Solution
实例进行交互,而不是与您创建的任意实例进行交互。 (如果您在内部类中,请使用Solution.this
访问周围对象的当前实例。)您的代码的有效版本是
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import netscape.javascript.JSObject;
public class Solution {
private JFXPanel jfxPanel;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Solution()::createUI);
}
private void createUI() {
JFrame f = new JFrame("panel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel();
jfxPanel = new JFXPanel();
createScene();
p.add(jfxPanel);
f.add(p);
f.setSize(300, 300);
f.setVisible(true);
}
private void createScene() {
Platform.setImplicitExit(false);
Platform.runLater(() -> {
BorderPane borderPane = new BorderPane();
WebView webComponent = new WebView();
WebEngine webEngine = webComponent.getEngine();
webEngine.documentProperty().addListener((obs, oldDoc, newDoc) -> {
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", this);
});
webEngine.load(Solution.class.getResource("/mypage.html").toString());
borderPane.setCenter(webComponent);
Scene scene = new Scene(borderPane, 300, 300);
jfxPanel.setScene(scene);
});
}
public void onClick() {
System.out.println("Invoked from JS");
}
}