Почему я не могу вызвать методы Java из содержимого html с помощью вызова JavaScript в JavaFX WebView? - PullRequest
1 голос
/ 26 мая 2020

Я работаю над задачей, которая должна вызвать метод java из содержимого html. Это приложение Swing, и я использовал 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>

Пожалуйста, дайте мне знать, что здесь нужно изменить

1 Ответ

1 голос
/ 26 мая 2020

Из документации , и класс, и метод, используемые для обратного вызова, должны быть public:

Обратный вызов Java из JavaScript

Метод JSObject.setMember полезен для включения восходящих вызовов из JavaScript в Java код, как показано в следующем примере. Код Java устанавливает новый объект JavaScript с именем app. Этот объект имеет один член publi c, метод exit.

public class JavaApplication {
    public void exit() {
        Platform.exit();
    }
}
...
JavaApplication javaApp = new JavaApplication();
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", javaApp);

...

Класс и метод Java должны быть объявлены publi c.

(Мое выделение)

Ваш Solution класс не является publi c, поэтому это не сработает.

Кроме того, при загрузке нового документа 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());

. Есть ряд других проблем с вашим кодом:

  1. JFrame должны быть построены на поток отправки событий AWT (то же правило применяется и к изменению компонентов, отображаемых в JFrame). Вы можете сделать это, заключив вызов createUI() в SwingUtilities.invokeLater(...).
  2. Непонятно, почему вы сделали Solution подклассом JFrame, , а также , создав новый JFrame в createUI(). Поскольку вы никогда не используете тот факт, что Solution подклассы JFrame, вы должны удалить это.
  3. PlatformImpl не является частью publi c API: следовательно, это было бы совершенно нормально для команды JavaFX чтобы удалить этот класс в более позднем выпуске. Вы должны использовать методы класса Platform.
  4. Вы почти наверняка хотите, чтобы обратный вызов Javascript взаимодействовал с текущим экземпляром 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");
    }

}
...