JavaFX WebView не работает с весенней загрузкой Maven - PullRequest
1 голос
/ 07 октября 2019

Я создал загрузочное JavaFX-приложение Spring с помощью Builder сцены. Когда я добавляю компонент WebView в свой файл FXML, я получаю следующую ошибку:

Caused by: javafx.fxml.LoadException: 
/C:/Users/pdhasar/WebProject/standalone-app/target/classes/WebView.fxml:12

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2605)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2583)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2413)
    at com.web.view.WebViewApplication.init(WebViewApplication.java:29)
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:841)
    ... 3 more
Caused by: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml.JavaFXBuilder$ObjectBuilder.build(JavaFXBuilderFactory.java:278)
    at javafx.fxml.FXMLLoader$ValueElement.processEndElement(FXMLLoader.java:759)
    at javafx.fxml.FXMLLoader.processEndElement(FXMLLoader.java:2827)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2536)
    ... 7 more
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
    at javafx.fxml.JavaFXBuilder$ObjectBuilder.build(JavaFXBuilderFactory.java:270)
    ... 10 more
Caused by: java.lang.IllegalStateException: Not on FX application thread; currentThread = JavaFX-Launcher
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:204)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:438)
    at javafx.scene.web.WebEngine.checkThread(WebEngine.java:1182)
    at javafx.scene.web.WebEngine.<init>(WebEngine.java:822)
    at javafx.scene.web.WebEngine.<init>(WebEngine.java:811)
    at javafx.scene.web.WebView.<init>(WebView.java:271)
    at javafx.scene.web.WebViewBuilder.build(WebViewBuilder.java:60)
    ... 21 more
2019-10-07 14:00:12.979  INFO 14680 --- [       Thread-6] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@7f904074: startup date [Mon Oct 07 14:00:10 IST 2019]; root of context hierarchy

Ниже приведен код с пружинной загрузкой: WebViewApplication:

package com.web.view;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan("com.web.view")
public class WebViewApplication extends Application {

    private ConfigurableApplicationContext context;
    private Parent rootNode;

    @Override
    public void init() throws Exception {
        SpringApplicationBuilder builder = new SpringApplicationBuilder(WebViewApplication.class);
        context = builder.run(getParameters().getRaw().toArray(new String[0]));
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/WebView.fxml"));
        loader.setControllerFactory(context::getBean);
        rootNode = loader.load();
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
        double width = visualBounds.getWidth();
        double height = visualBounds.getHeight();

        primaryStage.setScene(new Scene(rootNode, width, height));
        primaryStage.centerOnScreen();
        primaryStage.show();
    }

    @Override
    public void stop() throws Exception {
        context.close();
    }
}

WebViewController:

package com.web.view.controller;

import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import org.springframework.stereotype.Controller;

@Controller
public class WebViewController {

    @FXML private WebView webView;
    private WebEngine webEngine;

    @FXML
    private void handleWebViewButtonAction(ActionEvent event) {
        webEngine = webView.getEngine();
        webEngine.load("https://www.google.co.in");
    }
}

FXML:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.web.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.web.view.controller.WebViewController">
   <children>
      <WebView fx:id="webView" layoutX="-116.0" layoutY="-111.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
   </children>
</AnchorPane>

Кажется, что пружина пытается получить доступ к WebView, и в javafx есть ограничение, которое не позволяет Spring получить к нему доступ. Есть ли способ заставить WebView работать с весенней загрузкой. Может ли кто-нибудь помочь мне с этим.

Ответы [ 2 ]

1 голос
/ 08 октября 2019

Фон в потоках JavaFX

Система JavaFX ограничена тем, что не все элементы управления могут быть созданы из потока приложения JavaFX. Когда это имеет место для данного элемента управления или элемента пользовательского интерфейса, его документация отмечает это.

В частности, документация для WebView гласит:

Объекты WebView должны создаваться и доступны исключительно из потока FX.

Метод init() приложения JavaFX не выполняется в потоке приложения JavaFX. В моей среде (OS X, JavaFX 13) метод init() приложения JavaFX вызывается потоком с именем «JavaFX-Launcher». Как видно, если поместить следующий код в init():

System.out.println("Thread name: " + Thread.currentThread().getName());

Размещение того же кода в методе start() показывает, что он работает в другом потоке с именем «JavaFX». Тема приложения ". Это правильный поток для создания WebView.

Загрузчик FXML создает объекты на основе файла FXML. Если вы попытаетесь загрузить FXML из потока приложения JavaFX, и он содержит один из видов элементов управления, которые должны быть созданы в потоке приложения JavaFX, вы получите ошибку (как вы видите). Большинство элементов управления могут быть построены из потока JavaFX, поэтому в большинстве случаев это не проблема. Однако WebView должен быть должен быть создан в потоке приложения JavaFX, поэтому вы не можете загрузить документ FXML с WebView из потока приложения JavaFX.

Загрузите FXML, содержащий ссылку на WebView, из start(), а не init()

Решение состоит в том, чтобы переместить местоположение, в которое вы загружаете FXML, содержащий WebView, из метода init() вstart() метод (предложенный kleopatra в комментариях), как показано ниже:

import javafx.fxml.FXMLLoader;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan("com.web.view")
public class WebViewApplication extends Application {

    private ConfigurableApplicationContext context;
    private Parent rootNode;

    @Override
    public void init() throws Exception {
        SpringApplicationBuilder builder = new SpringApplicationBuilder(WebViewApplication.class);
        context = builder.run(getParameters().getRaw().toArray(new String[0]));
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/WebView.fxml"));
        loader.setControllerFactory(context::getBean);
        rootNode = loader.load();

        Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
        double width = visualBounds.getWidth();
        double height = visualBounds.getHeight();

        primaryStage.setScene(new Scene(rootNode, width, height));
        primaryStage.centerOnScreen();
        primaryStage.show();
    }

    @Override
    public void stop() throws Exception {
        context.close();
    }
}

Несвязанные рекомендации по проектированию

В качестве отступления я бы посоветовал делает ваше приложение JavaFX приложением Spring.

Вместо этого создайте отдельный класс для приложения Spring:

@SpringBootApplication
public class MySpringApplication {
    // spring application implementation.
}

И пусть ваше приложение JavaFX вызывает run() для SpringApplicationBuilder экземпляра, который был инициализирован вашим конкретным приложением Springкласс (т. е. в вашем методе JavaFX init() есть):

SpringApplicationBuilder builder = new SpringApplicationBuilder(
    MySpringApplication.class
);
context = builder.run(
    getParameters().getRaw().toArray(new String[0])
);

В противном случае у вас может быть два экземпляра приложения JavaFX, созданных во время выполнения (один приложением JavaFX), а другой - приложением Springбегун, который может сбивать с толку. Кроме того, разделив их, вы получите лучший дизайн за счет более четкого разделения интересов: приложение JavaFX отвечает за обработку событий жизненного цикла приложения JavaFX, а приложение Spring отвечает за события жизненного цикла Spring.

Затем вы можете запускать и тестировать свое весеннее приложение, полностью независимое от приложения JavaFX, что должно упростить тестирование и анализ кода.

0 голосов
/ 07 октября 2019

Наконец-то я получил решение. После того, как я добавил приведенный ниже код в метод init, он заработал

Platform.runLater(() -> { ... });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...