Hướng dẫn sử dụng JavaFX WebView và WebEngine
Công ty Vĩnh Cửu tuyển dụng lập trình viên Java

1- JavaFX WebView

JavaFX WebView là một trình duyệt mini, cũng được gọi là một trình duyệt được nhúng (embedded browser) vào trong ứng dụng JavaFX. Trình duyệt này được dựa trên WebKit, một bộ trình duyệt (browser engine) mã nguồn mở, nó hỗ trợ CSS, JavaScript, DOM và HTML5.
JavaFX WebView hỗ trợ bạn thực hiện các nhiệm vụ dưới đây trong ứng dụng JavaFX:
  • Biểu hiện nội dung HTML từ các URL địa phương và từ xa
  • Thu thập lịch sử web
  • Thực thi các lệnh Javascript
  • Thực hiện các cuộc gọi từ Javascript đến JavaFX
  • Quản lý web các cửa sổ pop-up
  • Áp dụng hiệu ứng cho trình duyệt nhúng
Tại phiên bản hiện tại ( JavaFX 2.3) thành phần WebView hỗ trợ các tính năng sau của HTML5:
  • Canvas
  • Media Playback
  • Các thẻ form (Ngoại trừ <input type="color"> )
  • Nội dung có thể chỉnh sửa
  • Bảo trì lịch sử
  • Hỗ trợ các thẻ <meter> và <progress>.
  • Hỗ trợ các thẻ <details> và <summary>.
  • DOM
  • SVG
  • Hỗ trợ các tên miền viết bằng các ngôn ngữ quốc tế.
Hình ảnh dưới đây là kiến trúc của trình duyệt nhúng trong JavaFX:

WebEngine

Class WebEngine cung cấp chức năng trang web cơ bản. Nó hỗ trợ tương tác người dùng như điều hướng liên kết và submit form HTML, mặc dù nó không tương tác với người sử dụng trực tiếp. Class WebEngine xử lý một trang web cùng một thời điểm. Nó hỗ trợ các tính năng duyệt web cơ bản tải nội dung HTML và truy cập vào DOM cũng như thực hiện các lệnh JavaScript.

WebView

Class WebView mở rộng từ class Node, nó bao lấy một đối tượng WebEngine và hiển thị nội dung Html. Bạn có thể lấy được đối tượng WebEngine từ WebView bằng cách sử dụng phương thức getEngine().
// Tạo một đối tượng WebView
WebView browser = new WebView();

// Lấy ra đối tượng WebEngine từ WebView
WebEngine webEngine = browser.getEngine();

// Tải trang
webEngine.load("http://eclipse.com");

2- Ví dụ với WebView

Tải từ một URL từ xa.
WebView browser = new WebView();
WebEngine webEngine = browser.getEngine();

String url = "https://eclipse.org";
 
// Tải một trang HTML từ url.
webEngine.load(url);
Ngoài việc hiển thị dữ liệu từ một URL từ xa, bạn cũng có thể hiển thị một nội dung HTML tĩnh.
// Một đoạn text html:

String html = "<html><h1>Hello</h1><h2>Hello</h2></html>";

// Tải nội dung html tĩnh.
webEngine.loadContent(html);

// Hoặc
webEngine.loadContent(html,"text/html");
Hoặc tải nội dung html từ một file địa phương.
File file = new File("C:/test/a.html");
URL url= file.toURI().toURL();

// file:/C:/test/a.html
webEngine.load(url.toString());
Xem ví dụ đầy đủ với WebView. Chú ý rằng WebView mặc định đã được đặt trong một ScrollPane, các scroll sẽ xuất hiện khi nội dung trang web lớn hơn vùng hiển thị.
WebViewDemo.java
package org.o7planning.javafx.webview;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class WebViewDemo extends Application {

   @Override
   public void start(final Stage stage) {

       Button buttonURL = new Button("Load Page https://eclipse.org");
       Button buttonHtmlString = new Button("Load HTML String");
       Button buttonHtmlFile = new Button("Load File C:/test/a.html");

       final WebView browser = new WebView();
       final WebEngine webEngine = browser.getEngine();

       buttonURL.setOnAction(new EventHandler<ActionEvent>() {

           @Override
           public void handle(ActionEvent event) {
               String url = "https://eclipse.org";
       
               // Tải một trang HTML từ url.
               webEngine.load(url);
           }
       });

       buttonHtmlString.setOnAction(new EventHandler<ActionEvent>() {

           @Override
           public void handle(ActionEvent event) {
               String html = "<html><h1>Hello</h1><h2>Hello</h2></html>";
         
               // Tải HTML String
               webEngine.loadContent(html);
           }
       });
       buttonHtmlFile.setOnAction(new EventHandler<ActionEvent>() {

           @Override
           public void handle(ActionEvent event) {
               try {
                   File file = new File("C:/test/a.html");
                   URL url = file.toURI().toURL();
                   // file:/C:/test/a.html
                   System.out.println("Local URL: " + url.toString());
                   webEngine.load(url.toString());
               } catch (MalformedURLException e) {
                   e.printStackTrace();
               }

           }
       });

       VBox root = new VBox();
       root.setPadding(new Insets(5));
       root.setSpacing(5);
       root.getChildren().addAll(buttonURL, buttonHtmlString, buttonHtmlFile, browser);

       Scene scene = new Scene(root);

       stage.setTitle("JavaFX WebView (o7planning.org)");
       stage.setScene(scene);
       stage.setWidth(450);
       stage.setHeight(300);

       stage.show();
   }

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

}

3- Ví dụ WebView với ProgressBar

Để tải một trang web lên trình duyệt cần mất một khoảng thời gian. Đôi khi bạn cần sử dụng một ProgressBar để hiển thị phần trăm mà trang web đã được tải.
WebViewWithProgressDemo.java
package org.o7planning.javafx.webview;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class WebViewWithProgressDemo extends Application {

  @Override
  public void start(final Stage stage) {

      TextField addressBar = new TextField();
      addressBar.setText("https://eclipse.org");
      Button goButton = new Button("Go!");
      Label stateLabel = new Label();

      stateLabel.setTextFill(Color.RED);
      ProgressBar progressBar = new ProgressBar();

      final WebView browser = new WebView();
      final WebEngine webEngine = browser.getEngine();

      // Worker làm nhiệm vụ tải trang web
      Worker<Void> worker = webEngine.getLoadWorker();

    
        // Lắng nghe các trạng thái tải trang web.
      worker.stateProperty().addListener(new ChangeListener<State>() {

          @Override
          public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
              stateLabel.setText("Loading state: " + newValue.toString());
              if (newValue == Worker.State.SUCCEEDED) {
                  stage.setTitle(webEngine.getLocation());
                  stateLabel.setText("Finish!");
              }
          }
      });

      // Kết nối thuộc tính progress của progressBar
      // với thuộc tính progressBar của Worker tải nội dung trang.
      progressBar.progressProperty().bind(worker.progressProperty());

      goButton.setOnAction(new EventHandler<ActionEvent>() {

          @Override
          public void handle(ActionEvent event) {
             String url = addressBar.getText();
              // Tải nội dung trang
              webEngine.load(url);
          }
      });
      //

      VBox root = new VBox();
      root.getChildren().addAll(addressBar, goButton, stateLabel, progressBar, browser);

      Scene scene = new Scene(root);

      stage.setTitle("JavaFX WebView (o7planning.org)");
      stage.setScene(scene);
      stage.setWidth(450);
      stage.setHeight(300);

      stage.show();
  }

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

}

4- Gọi Javascript từ JavaFX

Sau khi WebView load một trang web, bạn có thể tương tác với trang web từ JavaFX. Ví dụ dưới đây người dùng click vào một Button trên ứng dụng JavaFX nó gọi tới một hàm Javascript của trang đang hiển thị trên WebView.
// Bật JavaScript.
webEngine.setJavaScriptEnabled(true);

// Gọi một hàm Javascript của trang đang hiển thị trên WebView
webEngine.executeScript("changeBgColor();");A
WebViewExecuteJsDemo.java
package org.o7planning.javafx.webview;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class WebViewExecuteJsDemo extends Application {

 
   // Nội dung HTML với một hàm javascript.
   private static String HTML_STRING = //
           "<html>"//
                   + "<head> " //
                   + "  <script language='javascript'> " //
                   + "     function changeBgColor()  { "//
                   + "       var color= document.getElementById('color').value; "//
                   + "       document.body.style.backgroundColor= color; " //
                   + "     } " //
                   + "  </script> "//
                   + "</head> "//
                   + "<body> "//
                   + "   <h2>This is Html content</h2> "//
                   + "   <b>Enter Color:</b> "//
                   + "   <input id='color' value='yellow' /> "//
                   + "   <button onclick='changeBgColor();'>Change Bg Color</button> "//
                   + "</body> "//
                   + "</html> "//
   ;

   @Override
   public void start(final Stage stage) {

       Button button = new Button("Execute Javascript (Call from JavaFX)");

       final WebView browser = new WebView();
       final WebEngine webEngine = browser.getEngine();

 
       // Bật JavaScript.
       webEngine.setJavaScriptEnabled(true);

       webEngine.loadContent(HTML_STRING);

       button.setOnAction(new EventHandler<ActionEvent>() {

           @Override
           public void handle(ActionEvent event) {
 
               // Gọi một hàm Javascript của trang đang hiển thị trên WebView
               webEngine.executeScript("changeBgColor();");
           }
       });

       VBox root = new VBox();
       root.setPadding(new Insets(5));
       root.setSpacing(5);
       root.getChildren().addAll(button, browser);

       Scene scene = new Scene(root);

       stage.setTitle("JavaFX WebView (o7planning.org)");
       stage.setScene(scene);
       stage.setWidth(450);
       stage.setHeight(300);

       stage.show();
   }

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

}
Bạn cũng có thể truy cập vào các đối tượng Javascript thông qua các đối tượng Java. Hầu hết các đối tượng Javascript được gói bởi netscape.javascript.JSObject.  Các phương thức của JSObject:
public Object call(String methodName, Object... args);

public Object eval(String s);

public Object getMember(String name);

public void setMember(String name, Object value);

public void removeMember(String name);

public Object getSlot(int index);

public void setSlot(int index, Object value);
Ví dụ:
// Back lại trình duyệt.

webEngine.executeScript("history.back()");

// Hoặc:
// Lấy ra đối tượng JSObject đại diện cho đối tượng history của Javascript.
JSObject history = (JSObject) webEngine.executeScript("history");

// Gọi hàm back, không tham số.
history.call("back");
Một trường hợp đặc biệt khi mà gọi JavaScript trả về một đối tượng DOM ( DOM Node). Trong trường hợp này, kết quả được gói bởi đối tượng JSObject nó thi hành interface org.w3c.dom.Node.
Element p = (Element) ebEngine.executeScript("document.getElementById('para')");

p.setAttribute("style", "font-weight: bold");

5- Tạo cuộc gọi lên từ JavaScript tới JavaFX

Ở phần trên có thể gọi một hàm Javascript của trang đang hiển thị trên WebView từ JavaFX. Chiều ngược lại bạn cũng có thể tạo một cuộc gọi lên (Upcalls) từ Javascript tới JavaFX.
Ở phía JavaFX, bạn cần tạo một đối tượng giao tiếp (của một class) và đảm bảo nó được biết tới bởi JavaScript bằng cách gọi JSObject.setMember(). Sau khi thực hiện điều này, bạn có thể gọi các phương thức public hoặc các trường public của đối tượng Java từ Javascript.
// Một class cầu nối, phải là public
public class Bridge {

    public void showTime() {
        System.out.println("Show Time");

        label.setText("Now is: " + df.format(new Date()));
    }
}

// Lấy ra đối tượng window của trang
JSObject jsobj = (JSObject) webEngine.executeScript("window");

// Trong Javascript truy cập: window.myJavaMember....
jsobj.setMember("myJavaMember", new Bridge());
Trong trang HTML:
<!-- In HTML -->

<button onclick='myJavaMember.showTime();'>Call To JavaFX</button>
Xem ví dụ đầy đủ:
WebViewUpCallsDemo.java
package org.o7planning.javafx.webview;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;

public class WebViewUpCallsDemo extends Application {

   private DateFormat df = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");

   private Label label;

 
   // Một class cầu nối, phải là public
   public class Bridge {

       public void showTime() {
           System.out.println("Show Time");

           label.setText("Now is: " + df.format(new Date()));
       }
   }

 
   // Nội dung HTML với một hàm javascript.
   private static String HTML_STRING = //
           "<html>"//
                   + "<head> " //
                   + "  <script language='javascript'> " //
                   + "     function callToJavaFX()  { "//
                   + "        myJavaMember.showTime(); " //
                   + "     } " //
                   + "  </script> "//
                   + "</head> "//
                   + "<body> "//
                   + "   <h2>This is Html content</h2> "//
                   + "   <button onclick='callToJavaFX();'>Call To JavaFX</button> "//
                   + "</body> "//
                   + "</html> "//
   ;

   @Override
   public void start(final Stage stage) {

       label = new Label("-");

       final WebView browser = new WebView();
       final WebEngine webEngine = browser.getEngine();

 
       // Bật JavaScript.
       webEngine.setJavaScriptEnabled(true);

 
       // Worker làm nhiệm vụ tải trang web
       Worker<Void> worker = webEngine.getLoadWorker();
 
       // Lắng nghe các trạng thái tải trang web.
       worker.stateProperty().addListener(new ChangeListener<State>() {

           @Override
           public void changed(ObservableValue<? extends State> observable, //
                   State oldValue, State newValue) {

     
               // Khi trang web tải thành công
               if (newValue == Worker.State.SUCCEEDED) {
   
                   // Lấy ra đối tượng window của trang
                   JSObject jsobj = (JSObject) webEngine.executeScript("window");
 
                   // Set member cho đối tượng 'window'
                   // Trong Javascript truy cập: window.myJavaMember....
                   jsobj.setMember("myJavaMember", new Bridge());
               }
           }
       });

 
       // Tải nội dung HTML
       webEngine.loadContent(HTML_STRING);

       VBox root = new VBox();
       root.setPadding(new Insets(5));
       root.setSpacing(5);
       root.getChildren().addAll(label, browser);

       Scene scene = new Scene(root);

       stage.setTitle("JavaFX WebView (o7planning.org)");
       stage.setScene(scene);
       stage.setWidth(450);
       stage.setHeight(300);

       stage.show();
   }

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

}