如何仅将Angular 2+应用程序嵌入到JavaFX WebView中作为GUI?

时间:2018-06-21 10:02:44

标签: java angular javafx webview angular-cli

我正在开发一个简单的Java客户端应用程序,并且我想为其GUI使用HTML和Web应用程序框架,而不是使用Swing或JavaFX。

因此,我正在使用Angular 6(通过angular-cli生成)设计,构建和测试我的整个应用程序,我想使用JavaFX WebView和WebEngine组件指向我的Angular UI。

是否有任何简便/开箱即用/最佳实践的方式来连接所有内容?

我希望用户只是启动应用程序,甚至不意识到他正在浏览Webby。如果可能的话,我什至不会启动任何Web服务器。我只想要一个独立的应用程序,并且仅将角度框架用于GUI。

如果我必须启动Web服务器,我想在幕后这样做,并且不希望用户意识到。

问题

  • 我试图只使WebEngine指向内置的角度应用程序,但是this does not work与Angular-cli是开箱即用的。
  • 我尝试使用 com.sun.net.httpserver ,但是网络上的所有说明都只说了如何实现自己的虚拟HttpHandler,因此我无法理解如何绑定服务器。到我实际构建的Web应用程序(位于项目路径中的文件夹中)或如何将Web应用程序部署到HttpServer中,以正确的方式为准。

2 个答案:

答案 0 :(得分:2)

您可以使用Webkit嵌入任何HTML应用程序-jvm的Webkit版本是相对最新的,在我上次检查时,可能缺少一些功能,也许是WebGL。

要使其正常运行,请不要使用url: "{{ url('search/search') }}" 协议,而创建自己的Web服务器实在是太过分了。您只需要定义自己的协议并在Java层中声明它即可。

示例在这里:Registering and using a custom java.net.URL protocol

这是我的自定义协议实现:

file://

然后只需在javafx启动中注册协议:

/**
 * Register a protocol handler for URLs like this: <code>myapp:///pics/sland.gif</code><br>
 */
public class MyURLConnection extends URLConnection
{

    protected MyURLConnection(URL url) {
        super(url);
    }

    private byte[] data;

    @Override
    public void connect() throws IOException
    {
        if (connected)
        {
            return;
        }
        loadImage();
        connected = true;
    }

    public String getHeaderField(String name)
    {
        if ("Content-Type".equalsIgnoreCase(name))
        {
            return getContentType();
        }
        else if ("Content-Length".equalsIgnoreCase(name))
        {
            return "" + getContentLength();
        }
        return null;
    }

    public String getContentType()
    {
        String fileName = getURL().getFile();
        String ext = fileName.substring(fileName.lastIndexOf('.')+1);
        switch(ext){
        case "html":return "text/html";
        case "jpg":
        case "png":return "image/"+ext;
        case "js": return "application/javascript";
        }
        return "text/"+ext;
         // TODO: switch based on file-type "image/" + ext
    }

    public int getContentLength()
    {
        return data.length;
    }

    public long getContentLengthLong()
    {
        return data.length;
    }

    public boolean getDoInput()
    {
        return true;
    }

    public InputStream getInputStream() throws IOException
    {
        connect();
        return new ByteArrayInputStream(data);
    }

    /**
     * Reads all bytes from an input stream and writes them to an output stream.
     */
    private static long copy(InputStream source, OutputStream sink)
        throws IOException
    {
        long nread = 0L;
        byte[] buf = new byte[8192];
        int n;
        while ((n = source.read(buf)) > 0) {
            sink.write(buf, 0, n);
            nread += n;
        }
        return nread;
    }

    private void loadImage() throws IOException
    {
        if (data != null)
        {
            return;
        }

            String fileName = getURL().getFile();
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            copy(this.getClass().getResourceAsStream(fileName),byteArrayOutputStream);
           data = byteArrayOutputStream.toByteArray();

    }

    public OutputStream getOutputStream() throws IOException
    {
        // this might be unnecessary - the whole method can probably be omitted for our purposes
        return new ByteArrayOutputStream();
    }

    public java.security.Permission getPermission() throws IOException
    {
        return null; // we need no permissions to access this URL
    }

}

public class MyURLHandler extends URLStreamHandler
{

    @Override
    protected URLConnection openConnection(URL url) throws IOException
    {
        return new MyURLConnection(url);
    }

}

public class MyURLStreamHandlerFactory implements URLStreamHandlerFactory
{

    public URLStreamHandler createURLStreamHandler(String protocol)
    {
        if (protocol.equals("myapp"))
        {
            return new MyURLHandler();
        }
        return null;
    }

}

并在您的Webkit中使用URL public void start(Stage stage) { URL.setURLStreamHandlerFactory(new MyURLStreamHandlerFactory()); // etc. (不使用任何主机,使用任何您想要的内容或修改协议)

编辑: 我是在github上的项目中进行的(正在进行中):您只需要启动myapp://anyhost/yourfile.html主类。它使用src / main / resources的资源 https://github.com/pdemanget/blue-browser

答案 1 :(得分:0)

最终,我设法使用嵌入式Jetty为我的角度应用程序提供服务。

@Slf4j
public class MyEmbeddedAngularApp extends Application {

    private static final String APPLICATION_TITLE = "My Embedded Angular Application";

    /**
     * The service executing the server.
     */
    private ExecutorService executor = Executors.newSingleThreadExecutor();

    @Override
    public void start(Stage stage) throws Exception {
        startServer();

        //Create the root AnchorPane
        AnchorPane root = new AnchorPane();

        //Initialize the Web UI
        WebView browser = new WebView();
        WebEngine webEngine = browser.getEngine();
        webEngine.load("http://localhost:2342");

        //add WebView to root
        root.getChildren().add(browser);

        //Setting up the scene
        Scene scene = new Scene(root);
        stage.setTitle(APPLICATION_TITLE);
        stage.setScene(scene);

        //Let the show begin!
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

    private void startServer() throws Exception {
        Server server = new Server(2342);

        WebAppContext webapp = new WebAppContext();
        webapp.setContextPath("/");
        File warFile = new File("./src/main/resources/my-app/dist/my-app");
        webapp.setWar(warFile.getAbsolutePath());

        server.setHandler(webapp);
        server.setStopAtShutdown(true);

        server.start();

        server.dumpStdErr();

        executor.submit(() -> {
            try {
                server.join();
            } catch (InterruptedException e) {
                log.error("Could not start the server");
            }
        });
    }

}