JavaFX的WebView中通过JavaScript回调Java的程序

解决webview首次加载的html页面无法使用js调用java程序问题。

根据官网给出的例子,自己写了个总是调用不成功。以下是官网例子代码:

http://docs.oracle.com/javase/8/javafx/embedded-browser-tutorial/js-javafx.htm

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker.State;
import javafx.event.ActionEvent;
import javafx.geometry.HPos;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Hyperlink;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;
 
public class WebViewSample extends Application {
 
    private Scene scene;
 
    @Override
    public void start(Stage stage) {
        // create scene
        stage.setTitle("Web View Sample");
        scene = new Scene(new Browser(stage), 900, 600, Color.web("#666970"));
        stage.setScene(scene);
        // apply CSS style
        scene.getStylesheets().add("webviewsample/BrowserToolbar.css");
        // show stage
        stage.show();
    }
 
    public static void main(String[] args) {
        launch(args);
    }
}
 
class Browser extends Region {
 
    private final HBox toolBar;
    final private static String[] imageFiles = new String[]{
        "product.png",
        "blog.png",
        "documentation.png",
        "partners.png",
        "help.png"
    };
    final private static String[] captions = new String[]{
        "Products",
        "Blogs",
        "Documentation",
        "Partners",
        "Help"
    };
    final private static String[] urls = new String[]{
        "http://www.oracle.com/products/index.html",
        "http://blogs.oracle.com/",
        "http://docs.oracle.com/javase/index.html",
        "http://www.oracle.com/partners/index.html",
        WebViewSample.class.getResource("help.html").toExternalForm()
    };
    final ImageView selectedImage = new ImageView();
    final Hyperlink[] hpls = new Hyperlink[captions.length];
    final Image[] images = new Image[imageFiles.length];
    final WebView browser = new WebView();
    final WebEngine webEngine = browser.getEngine();
    final Button toggleHelpTopics = new Button("Toggle Help Topics");
    private boolean needDocumentationButton = false;
    
    
    public Browser(final Stage stage) {
        //apply the styles
        getStyleClass().add("browser");
                
        for (int i = 0; i < captions.length; i++) {
            // create hyperlinks
            Hyperlink hpl = hpls[i] = new Hyperlink(captions[i]);
            Image image = images[i]
                    = new Image(getClass().getResourceAsStream(imageFiles[i]));
            hpl.setGraphic(new ImageView(image));
            final String url = urls[i];
            final boolean addButton = (hpl.getText().equals("Help"));  
            
            // process event 
            hpl.setOnAction((ActionEvent e) -> {
                needDocumentationButton = addButton;
                webEngine.load(url);
            });
                    
        }
 
        // create the toolbar
        toolBar = new HBox();
        toolBar.setAlignment(Pos.CENTER);
        toolBar.getStyleClass().add("browser-toolbar");
        toolBar.getChildren().addAll(hpls);
        toolBar.getChildren().add(createSpacer());
 
        //set action for the button
        toggleHelpTopics.setOnAction((ActionEvent t) -> {
            webEngine.executeScript("toggle_visibility('help_topics')");
        });
 
         // process page loading
        webEngine.getLoadWorker().stateProperty().addListener(
            (ObservableValue ov, State oldState, 
                State newState) -> {
                    toolBar.getChildren().remove(toggleHelpTopics);
                    if (newState == State.SUCCEEDED) {
                        JSObject win
                                = (JSObject) webEngine.executeScript("window");
                        win.setMember("app", new JavaApp());
                        if (needDocumentationButton) {
                            toolBar.getChildren().add(toggleHelpTopics);
                        }
                    }
        });
 
        // load the home page        
        webEngine.load("http://www.oracle.com/products/index.html");
 
        //add components
        getChildren().add(toolBar);
        getChildren().add(browser);
    }
    
    // JavaScript interface object
    public class JavaApp {
 
        public void exit() {
            Platform.exit();
        }
    }
 
    private Node createSpacer() {
        Region spacer = new Region();
        HBox.setHgrow(spacer, Priority.ALWAYS);
        return spacer;
    }
 
    @Override
    protected void layoutChildren() {
        double w = getWidth();
        double h = getHeight();
        double tbHeight = toolBar.prefHeight(w);
        layoutInArea(browser,0,0,w,h-tbHeight,0,HPos.CENTER,VPos.CENTER);
        layoutInArea(toolBar,0,h-tbHeight,w,tbHeight,0,HPos.CENTER,VPos.CENTER);
    }
 
    @Override
    protected double computePrefWidth(double height) {
        return 900;
    }
 
    @Override
    protected double computePrefHeight(double width) {
        return 600;
    }
}


Example 5-1 help.html file


    
        
        
    
    
        

Online Help

[+] Show/Hide Help Topics

Exit the Application




弄了几个小时,最后怀疑是不是javafx版本问题,我的版本和官网给的例子不一致导致的。

于是我就把官网的例子代码拷贝下来,直接使用它的例子,居然成功了。看来是程序哪里出问题了。

接下来就是不停的对照代码,希望能发现问题所在。又是2个多小时过去了,终于找到问题所在了,原来要右键点击重新加载一次页面,js才能调用java程序。

好奇怪的现象!!!官网例子不用重新加载是因为进入help.html页面是通过点击工具栏上的help按钮,浏览器才加载进去的,浏览器首次加载的是Oracle首页。

如果把官网例子

webEngine.load("http://www.oracle.com/products/index.html");

改为

webEngine.load(WebViewSample.class.getResource("help.html").toExternalForm());

那么一样也会出现点击超链接后不会退出程序,而是会进入一个空白页面,然后需要鼠标右击选择退回,再次点击超链接才能退出程序。

也就是说help.html页面要第二次加载js中才能调用java程序。

下面是我写的例子的解决方案。

package application.view;

import application.Main;
import application.util.ConfigUtil;
import application.util.OpenAppUtil;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.fxml.FXML;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import netscape.javascript.JSObject;

/**
 * 根面板控制器
 * @author pelin
 */
public class RootLayoutController {
	@FXML
	WebView browser;
	
	WebEngine webEngine;
	/**
     * 初始化,类似与构造函数. fxml加载完成后自动调用该方法
     */
    @FXML
    private void initialize() {
    	webEngine = browser.getEngine();
    	OpenAppUtil openAppUtil = new OpenAppUtil();
    	webEngine.getLoadWorker().stateProperty().addListener(
                new ChangeListener() {
                  @Override public void changed(ObservableValue ov, State oldState, State newState) {
                      if (newState == Worker.State.SUCCEEDED) {
//                        stage.setTitle(webEngine.getLocation());
//                    	  System.out.println(newState);
                    	  JSObject win = (JSObject) webEngine.executeScript("window");
                		  win.setMember("openAppUtil", openAppUtil);
                      }
                      else if (newState == Worker.State.FAILED){
//                    	  System.out.println(newState);
//                    	  webEngine.load(HtmlPath.class.getResource("home.html").toExternalForm());
                      }
                      else{
//                    	  System.out.println(newState);
                      }
                    }
                });
//    	webEngine.load(ConfigUtil.getValue("homePage"));
    	webEngine.load(Main.class.getResource("view/test.html").toExternalForm());
//    	webEngine.load("http://10.100.1.240:8089/test.html");
    	
    }
    
}

主要修改的地方是:

win.setMember("openAppUtil", new OpenAppUtil());

改为

win.setMember("openAppUtil", openAppUtil);

并且在webEngine.getLoadWorker().stateProperty().addListener之前创建openAppUtil对象。即:OpenAppUtil openAppUtil = new OpenAppUtil();

这样webview在首次加载html后就可以调用java程序了。








你可能感兴趣的:(javafx)