Overview
JavaFX WebView allows you to embed web content in Java applications, create hybrid desktop apps, and bridge Java and JavaScript communication.
Basic WebView Setup
1. Simple WebView Application
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class BasicWebViewApp extends Application {
@Override
public void start(Stage primaryStage) {
WebView webView = new WebView();
// Load a web page
webView.getEngine().load("https://www.example.com");
// Configure WebView settings
webView.setPrefSize(1200, 800);
webView.getEngine().setJavaScriptEnabled(true);
Scene scene = new Scene(webView);
primaryStage.setTitle("JavaFX WebView Demo");
primaryStage.setScene(scene);
primaryStage.show();
// Handle window closing
primaryStage.setOnCloseRequest(event -> {
System.out.println("Application closing...");
});
}
public static void main(String[] args) {
launch(args);
}
}
2. Enhanced WebView with Controls
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class BrowserApp extends Application {
private WebView webView;
private TextField addressBar;
private Button backButton, forwardButton, refreshButton;
private Label statusLabel;
@Override
public void start(Stage primaryStage) {
// Create WebView
webView = new WebView();
webView.setPrefSize(1200, 800);
// Create toolbar
ToolBar toolbar = createToolbar();
// Create status bar
statusLabel = new Label("Ready");
// Layout
BorderPane root = new BorderPane();
root.setTop(toolbar);
root.setCenter(webView);
root.setBottom(createStatusBar());
Scene scene = new Scene(root, 1200, 800);
primaryStage.setTitle("JavaFX Browser");
primaryStage.setScene(scene);
primaryStage.show();
// Load initial page
loadUrl("https://www.google.com");
}
private ToolBar createToolbar() {
// Navigation buttons
backButton = new Button("←");
forwardButton = new Button("→");
refreshButton = new Button("↻");
backButton.setOnAction(e -> webView.getEngine().getHistory().go(-1));
forwardButton.setOnAction(e -> webView.getEngine().getHistory().go(1));
refreshButton.setOnAction(e -> webView.getEngine().reload());
// Address bar
addressBar = new TextField();
addressBar.setPromptText("Enter URL...");
addressBar.setOnAction(e -> loadUrl(addressBar.getText()));
// Additional controls
Button goButton = new Button("Go");
goButton.setOnAction(e -> loadUrl(addressBar.getText()));
HBox navigationBox = new HBox(5, backButton, forwardButton, refreshButton);
HBox addressBox = new HBox(5, addressBar, goButton);
addressBox.setHgrow(addressBar, javafx.scene.layout.Priority.ALWAYS);
return new ToolBar(navigationBox, new Separator(), addressBox);
}
private HBox createStatusBar() {
HBox statusBar = new HBox(10);
statusBar.setPadding(new Insets(5));
statusBar.getChildren().add(statusLabel);
return statusBar;
}
private void loadUrl(String url) {
if (url == null || url.trim().isEmpty()) {
return;
}
// Add protocol if missing
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "https://" + url;
}
try {
webView.getEngine().load(url);
addressBar.setText(url);
statusLabel.setText("Loading: " + url);
} catch (Exception e) {
statusLabel.setText("Error loading: " + e.getMessage());
}
}
public static void main(String[] args) {
launch(args);
}
}
JavaScript-Java Communication
3. Bridge Between Java and JavaScript
import javafx.application.Application;
import javafx.concurrent.Worker;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;
public class JavaJSBridge extends Application {
private WebView webView;
private JavaBridge javaBridge;
@Override
public void start(Stage primaryStage) {
webView = new WebView();
javaBridge = new JavaBridge();
// Enable JavaScript
webView.getEngine().setJavaScriptEnabled(true);
// Set up bridge when page loads
webView.getEngine().getLoadWorker().stateProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue == Worker.State.SUCCEEDED) {
JSObject window = (JSObject) webView.getEngine().executeScript("window");
window.setMember("javaBridge", javaBridge);
}
});
// Load HTML with JavaScript
String htmlContent = """
<!DOCTYPE html>
<html>
<head>
<title>Java-JS Bridge</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
button { padding: 10px; margin: 5px; }
input { padding: 8px; margin: 5px; width: 200px; }
.result { margin: 10px; padding: 10px; border: 1px solid #ccc; }
</style>
</head>
<body>
<h2>Java-JavaScript Communication</h2>
<div>
<input type="text" id="messageInput" placeholder="Enter message for Java">
<button onclick="sendToJava()">Send to Java</button>
</div>
<div>
<button onclick="callJavaMethod()">Call Java Method</button>
<button onclick="getSystemInfo()">Get System Info</button>
<button onclick="showDialog()">Show Java Dialog</button>
</div>
<div id="result" class="result"></div>
<script>
function sendToJava() {
const input = document.getElementById('messageInput').value;
if (input) {
const result = javaBridge.handleMessage(input);
displayResult('Java response: ' + result);
}
}
function callJavaMethod() {
const result = javaBridge.performCalculation(10, 20);
displayResult('Calculation result: ' + result);
}
function getSystemInfo() {
const info = javaBridge.getSystemInformation();
displayResult('System info: ' + JSON.stringify(info));
}
function showDialog() {
javaBridge.showAlert('Hello from JavaScript!');
}
function displayResult(message) {
document.getElementById('result').innerHTML = message;
}
// Function that Java can call
function updateFromJava(message) {
displayResult('Message from Java: ' + message);
}
function showUserData(userData) {
displayResult('User data from Java: ' + JSON.stringify(userData));
}
</script>
</body>
</html>
""";
webView.getEngine().loadContent(htmlContent);
BorderPane root = new BorderPane(webView);
Scene scene = new Scene(root, 800, 600);
primaryStage.setTitle("Java-JS Bridge Demo");
primaryStage.setScene(scene);
primaryStage.show();
}
// Java class that will be exposed to JavaScript
public class JavaBridge {
public String handleMessage(String message) {
System.out.println("Received from JavaScript: " + message);
return "Java processed: " + message.toUpperCase();
}
public int performCalculation(int a, int b) {
return a + b;
}
public String getSystemInformation() {
return "Java Version: " + System.getProperty("java.version") +
", OS: " + System.getProperty("os.name");
}
public void showAlert(String message) {
javafx.application.Platform.runLater(() -> {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Message from JavaScript");
alert.setHeaderText(null);
alert.setContentText(message);
alert.showAndWait();
});
}
// Method to call JavaScript from Java
public void sendMessageToJavaScript(String message) {
javafx.application.Platform.runLater(() -> {
webView.getEngine().executeScript("updateFromJava('" + message + "')");
});
}
public void sendUserDataToJS(String name, String email) {
javafx.application.Platform.runLater(() -> {
String script = String.format("showUserData({name: '%s', email: '%s'})", name, email);
webView.getEngine().executeScript(script);
});
}
}
public static void main(String[] args) {
launch(args);
}
}
4. Advanced Two-Way Communication
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;
public class AdvancedBridgeApp extends Application {
private WebView webView;
private TextArea javaLog;
private TextField jsMessageField;
private AdvancedBridge advancedBridge;
@Override
public void start(Stage primaryStage) {
webView = new WebView();
advancedBridge = new AdvancedBridge();
setupWebEngine();
setupUI(primaryStage);
// Load interactive HTML page
loadInteractiveHTML();
}
private void setupWebEngine() {
webView.getEngine().setJavaScriptEnabled(true);
// Handle JavaScript alerts
webView.getEngine().setOnAlert(event -> {
log("JavaScript Alert: " + event.getData());
});
// Handle JavaScript errors
webView.getEngine().getLoadWorker().exceptionProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue != null) {
log("JavaScript Error: " + newValue.getMessage());
}
});
// Set up bridge when page loads
webView.getEngine().getLoadWorker().stateProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue == javafx.concurrent.Worker.State.SUCCEEDED) {
JSObject window = (JSObject) webView.getEngine().executeScript("window");
window.setMember("advancedBridge", advancedBridge);
log("JavaScript bridge established");
}
});
}
private void setupUI(Stage stage) {
// Java controls panel
VBox javaPanel = new VBox(10);
javaPanel.setPadding(new javafx.geometry.Insets(10));
javaPanel.setPrefWidth(300);
Label javaLabel = new Label("Java Controls");
jsMessageField = new TextField();
jsMessageField.setPromptText("Message for JavaScript");
Button sendToJSButton = new Button("Send to JavaScript");
sendToJSButton.setOnAction(e -> sendMessageToJS());
Button callJSFunctionButton = new Button("Call JS Function");
callJSFunctionButton.setOnAction(e -> callJSFunction());
Button manipulateDOMButton = new Button("Manipulate DOM");
manipulateDOMButton.setOnAction(e -> manipulateDOM());
javaLog = new TextArea();
javaLog.setPrefHeight(200);
javaLog.setEditable(false);
javaPanel.getChildren().addAll(
javaLabel, jsMessageField, sendToJSButton,
callJSFunctionButton, manipulateDOMButton,
new Label("Java Log:"), javaLog
);
// Main layout
BorderPane root = new BorderPane();
root.setCenter(webView);
root.setRight(javaPanel);
Scene scene = new Scene(root, 1200, 800);
stage.setTitle("Advanced Java-JS Bridge");
stage.setScene(scene);
stage.show();
}
private void loadInteractiveHTML() {
String html = """
<!DOCTYPE html>
<html>
<head>
<title>Advanced Bridge</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
background: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.control-group {
margin: 15px 0;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
}
button {
padding: 8px 15px;
margin: 5px;
background: #007cba;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover { background: #005a87; }
input, select {
padding: 8px;
margin: 5px;
border: 1px solid #ddd;
border-radius: 4px;
}
.result {
margin: 10px 0;
padding: 10px;
background: #e9f7ff;
border-left: 4px solid #007cba;
}
.user-display {
padding: 10px;
background: #f0f8ff;
border: 1px solid #b3d9ff;
margin: 10px 0;
}
</style>
</head>
<body>
<div class="container">
<h1>Advanced Java-JS Communication</h1>
<div class="control-group">
<h3>Data Exchange</h3>
<input type="text" id="dataInput" placeholder="Enter data">
<button onclick="sendDataToJava()">Send Data to Java</button>
<button onclick="getJavaData()">Get Data from Java</button>
<div id="dataResult" class="result"></div>
</div>
<div class="control-group">
<h3>User Management</h3>
<input type="text" id="userName" placeholder="Name">
<input type="email" id="userEmail" placeholder="Email">
<button onclick="createUser()">Create User in Java</button>
<button onclick="loadUsers()">Load Users from Java</button>
<div id="userResult" class="result"></div>
<div id="userDisplay"></div>
</div>
<div class="control-group">
<h3>DOM Manipulation from Java</h3>
<div id="javaControlledArea">
<p>This area can be controlled from Java</p>
</div>
<button onclick="prepareForJavaDOM()">Prepare for Java DOM</button>
</div>
</div>
<script>
// Data exchange functions
function sendDataToJava() {
const data = document.getElementById('dataInput').value;
if (data) {
const response = advancedBridge.processData(data);
document.getElementById('dataResult').innerHTML =
'<strong>Java Response:</strong> ' + response;
}
}
function getJavaData() {
const data = advancedBridge.getApplicationData();
document.getElementById('dataResult').innerHTML =
'<strong>Data from Java:</strong> ' + JSON.stringify(data);
}
// User management functions
function createUser() {
const name = document.getElementById('userName').value;
const email = document.getElementById('userEmail').value;
if (name && email) {
const user = { name: name, email: email };
const result = advancedBridge.saveUser(user);
document.getElementById('userResult').innerHTML =
'<strong>Save Result:</strong> ' + result;
}
}
function loadUsers() {
const users = advancedBridge.loadAllUsers();
displayUsers(users);
}
function displayUsers(users) {
const display = document.getElementById('userDisplay');
display.innerHTML = '<h4>Users from Java:</h4>';
if (users && users.length > 0) {
users.forEach(user => {
const userDiv = document.createElement('div');
userDiv.className = 'user-display';
userDiv.innerHTML = `
<strong>Name:</strong> ${user.name}<br>
<strong>Email:</strong> ${user.email}<br>
<strong>ID:</strong> ${user.id}
`;
display.appendChild(userDiv);
});
} else {
display.innerHTML += '<p>No users found</p>';
}
}
function prepareForJavaDOM() {
document.getElementById('javaControlledArea').innerHTML =
'<p>Ready for Java DOM manipulation</p>';
}
// Functions called from Java
function updateContentFromJava(content) {
document.getElementById('javaControlledArea').innerHTML = content;
}
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 15px;
background: ${type === 'error' ? '#ff4444' : '#44ff44'};
color: white;
border-radius: 5px;
z-index: 1000;
`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
document.body.removeChild(notification);
}, 3000);
}
function executeComplexOperation(data) {
// Simulate complex JavaScript operation
return new Promise((resolve) => {
setTimeout(() => {
resolve({
original: data,
processed: data.toUpperCase(),
timestamp: new Date().toISOString(),
hash: Math.random().toString(36).substr(2, 9)
});
}, 1000);
});
}
</script>
</body>
</html>
""";
webView.getEngine().loadContent(html);
}
private void sendMessageToJS() {
String message = jsMessageField.getText();
if (!message.isEmpty()) {
advancedBridge.sendMessageToJavaScript(message);
jsMessageField.clear();
}
}
private void callJSFunction() {
// Call a JavaScript function that returns a promise
webView.getEngine().executeScript("""
executeComplexOperation('Hello from Java')
.then(result => {
showNotification('Operation completed: ' + result.processed);
advancedBridge.handleAsyncResult(result);
});
""");
}
private void manipulateDOM() {
String htmlContent = """
<div style="padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px;">
<h3>This content was injected from Java!</h3>
<p>Current time: """ + new java.util.Date() + """</p>
<p>Java version: """ + System.getProperty("java.version") + """</p>
<button onclick="alert('Button clicked!')">Test Button</button>
</div>
""";
advancedBridge.updateDOMContent(htmlContent);
}
private void log(String message) {
Platform.runLater(() -> {
javaLog.appendText(message + "\n");
});
}
// Advanced bridge class
public class AdvancedBridge {
private int userCounter = 1;
private java.util.List<java.util.Map<String, String>> users = new java.util.ArrayList<>();
public String processData(String data) {
log("Processing data from JS: " + data);
return "Processed: " + data.toUpperCase() + " (length: " + data.length() + ")";
}
public java.util.Map<String, Object> getApplicationData() {
java.util.Map<String, Object> data = new java.util.HashMap<>();
data.put("timestamp", new java.util.Date().toString());
data.put("javaVersion", System.getProperty("java.version"));
data.put("os", System.getProperty("os.name"));
data.put("userCount", users.size());
data.put("memory", Runtime.getRuntime().totalMemory() / (1024 * 1024) + " MB");
return data;
}
public String saveUser(java.util.Map<String, String> userData) {
userData.put("id", String.valueOf(userCounter++));
users.add(userData);
log("User saved: " + userData);
return "User saved successfully with ID: " + userData.get("id");
}
public java.util.List<java.util.Map<String, String>> loadAllUsers() {
log("Loading all users: " + users.size() + " found");
return new java.util.ArrayList<>(users);
}
public void handleAsyncResult(java.util.Map<String, Object> result) {
log("Async operation completed: " + result);
}
// Methods to call JavaScript from Java
public void sendMessageToJavaScript(String message) {
Platform.runLater(() -> {
webView.getEngine().executeScript(
"showNotification('Message from Java: " + message + "')");
});
}
public void updateDOMContent(String html) {
Platform.runLater(() -> {
// Escape the HTML content for JavaScript
String escapedHtml = html.replace("'", "\\'").replace("\n", "\\n");
webView.getEngine().executeScript(
"updateContentFromJava('" + escapedHtml + "')");
});
}
public void showError(String message) {
Platform.runLater(() -> {
webView.getEngine().executeScript(
"showNotification('" + message + "', 'error')");
});
}
}
public static void main(String[] args) {
launch(args);
}
}
5. WebView Event Handling and Customization
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.concurrent.Worker;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class WebViewEventsApp extends Application {
private WebView webView;
private TextArea eventLog;
private ProgressBar progressBar;
private Label statusLabel;
@Override
public void start(Stage primaryStage) {
webView = new WebView();
setupWebEngineListeners();
setupUI(primaryStage);
// Load initial page
webView.getEngine().load("https://www.example.com");
}
private void setupWebEngineListeners() {
// Progress monitoring
webView.getEngine().getLoadWorker().progressProperty().addListener(
(observable, oldValue, newValue) -> {
progressBar.setProgress(newValue.doubleValue());
});
// Title change listener
webView.getEngine().titleProperty().addListener(
(observable, oldValue, newValue) -> {
log("Title changed: " + newValue);
});
// Location change listener
webView.getEngine().locationProperty().addListener(
(observable, oldValue, newValue) -> {
log("Location changed: " + newValue);
statusLabel.setText("Loading: " + newValue);
});
// State change listener
webView.getEngine().getLoadWorker().stateProperty().addListener(
(observable, oldValue, newValue) -> {
log("State changed: " + oldValue + " -> " + newValue);
switch (newValue) {
case RUNNING:
statusLabel.setText("Loading...");
break;
case SUCCEEDED:
statusLabel.setText("Load completed");
break;
case FAILED:
statusLabel.setText("Load failed");
break;
case CANCELLED:
statusLabel.setText("Load cancelled");
break;
}
});
// JavaScript console messages
webView.getEngine().setOnError(event -> {
log("JavaScript Error: " + event.getMessage());
});
// Context menu customization
webView.setContextMenuEnabled(false); // Disable default context menu
setupCustomContextMenu();
}
private void setupCustomContextMenu() {
ContextMenu contextMenu = new ContextMenu();
MenuItem backItem = new MenuItem("Back");
backItem.setOnAction(e -> webView.getEngine().getHistory().go(-1));
MenuItem forwardItem = new MenuItem("Forward");
forwardItem.setOnAction(e -> webView.getEngine().getHistory().go(1));
MenuItem reloadItem = new MenuItem("Reload");
reloadItem.setOnAction(e -> webView.getEngine().reload());
MenuItem inspectItem = new MenuItem("Inspect Page");
inspectItem.setOnAction(e -> showPageInfo());
MenuItem viewSourceItem = new MenuItem("View Source");
viewSourceItem.setOnAction(e -> showPageSource());
contextMenu.getItems().addAll(
backItem, forwardItem, reloadItem,
new SeparatorMenuItem(),
inspectItem, viewSourceItem
);
webView.setOnContextMenuRequested(event -> {
contextMenu.show(webView, event.getScreenX(), event.getScreenY());
});
}
private void showPageInfo() {
String info = """
Current URL: %s
Title: %s
JavaScript Enabled: %s
User Agent: %s
""".formatted(
webView.getEngine().getLocation(),
webView.getEngine().getTitle(),
webView.getEngine().isJavaScriptEnabled(),
webView.getEngine().getUserAgent()
);
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Page Information");
alert.setHeaderText("Current Page Details");
alert.setContentText(info);
alert.showAndWait();
}
private void showPageSource() {
try {
String source = (String) webView.getEngine().executeScript("document.documentElement.outerHTML");
TextArea sourceArea = new TextArea(source);
sourceArea.setEditable(false);
ScrollPane scrollPane = new ScrollPane(sourceArea);
scrollPane.setPrefSize(800, 600);
Stage sourceStage = new Stage();
sourceStage.setTitle("Page Source - " + webView.getEngine().getLocation());
sourceStage.setScene(new Scene(scrollPane));
sourceStage.show();
} catch (Exception e) {
log("Error getting page source: " + e.getMessage());
}
}
private void setupUI(Stage stage) {
// Control panel
VBox controlPanel = new VBox(10);
controlPanel.setPadding(new javafx.geometry.Insets(10));
controlPanel.setPrefWidth(300);
// Progress bar
progressBar = new ProgressBar(0);
// Status label
statusLabel = new Label("Ready");
// Navigation controls
Button backBtn = new Button("Back");
backBtn.setOnAction(e -> webView.getEngine().getHistory().go(-1));
Button forwardBtn = new Button("Forward");
forwardBtn.setOnAction(e -> webView.getEngine().getHistory().go(1));
Button reloadBtn = new Button("Reload");
reloadBtn.setOnAction(e -> webView.getEngine().reload());
TextField urlField = new TextField();
urlField.setPromptText("Enter URL");
urlField.setOnAction(e -> {
String url = urlField.getText();
if (!url.isEmpty()) {
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "https://" + url;
}
webView.getEngine().load(url);
}
});
// Bind URL field to current location
webView.getEngine().locationProperty().addListener(
(observable, oldValue, newValue) -> {
urlField.setText(newValue);
});
// Event log
eventLog = new TextArea();
eventLog.setPrefHeight(300);
eventLog.setEditable(false);
Button clearLogBtn = new Button("Clear Log");
clearLogBtn.setOnAction(e -> eventLog.clear());
controlPanel.getChildren().addAll(
new Label("Navigation:"),
new javafx.scene.layout.HBox(5, backBtn, forwardBtn, reloadBtn),
new Label("URL:"),
urlField,
new Label("Progress:"),
progressBar,
statusLabel,
new Label("Event Log:"),
eventLog,
clearLogBtn
);
// Main layout
BorderPane root = new BorderPane();
root.setCenter(webView);
root.setRight(controlPanel);
Scene scene = new Scene(root, 1200, 800);
stage.setTitle("WebView with Event Handling");
stage.setScene(scene);
stage.show();
}
private void log(String message) {
javafx.application.Platform.runLater(() -> {
String timestamp = java.time.LocalTime.now().format(
java.time.format.DateTimeFormatter.ofPattern("HH:mm:ss"));
eventLog.appendText("[" + timestamp + "] " + message + "\n");
});
}
public static void main(String[] args) {
launch(args);
}
}
Key Features Covered:
- Basic WebView Integration - Loading web content
- Browser-like Controls - Navigation, address bar, refresh
- Java-JavaScript Bridge - Two-way communication
- Advanced Communication - Complex data exchange, DOM manipulation
- Event Handling - Progress monitoring, state changes, errors
- Customization - Context menus, progress bars, logging
This comprehensive WebView integration allows you to create powerful hybrid applications that combine web technologies with Java's desktop capabilities.