javafx webview / webengine上的自定义上下文菜单

如何在WebEngine javafx中为文档的整个条目设置自定义上下文菜单?
像这样的东西

 +------------+ |Reload | |Save page | |Hide Images | +------------+ 

我喜欢调用并显示整个文档条目的上下文弹出窗口(每个节点都相同)。 谢谢。

我没有看到与默认上下文菜单交互的方法。 但是,禁用它并实现自己的并不困难。

禁用默认上下文菜单

 webView.setContextMenuEnabled(); 

然后创建自己的上下文菜单,并使用Web视图注册鼠标侦听器以在右键单击时显示它:

 import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.ContextMenu; import javafx.scene.control.MenuItem; import javafx.scene.control.TextField; import javafx.scene.input.MouseButton; import javafx.scene.layout.BorderPane; import javafx.scene.web.WebView; import javafx.stage.Stage; public class WebViewContextMenuTest extends Application { private final String START_URL = "http://stackoverflow.com/questions/27047447/customized-context-menu-on-javafx-webview-webengine/27047830#27047830"; @Override public void start(Stage primaryStage) { TextField locationField = new TextField(START_URL); WebView webView = new WebView(); webView.getEngine().load(START_URL); webView.setContextMenuEnabled(false); createContextMenu(webView); locationField.setOnAction(e -> { webView.getEngine().load(getUrl(locationField.getText())); }); BorderPane root = new BorderPane(webView, locationField, null, null, null); primaryStage.setScene(new Scene(root, 800, 600)); primaryStage.show(); } private void createContextMenu(WebView webView) { ContextMenu contextMenu = new ContextMenu(); MenuItem reload = new MenuItem("Reload"); reload.setOnAction(e -> webView.getEngine().reload()); MenuItem savePage = new MenuItem("Save Page"); savePage.setOnAction(e -> System.out.println("Save page...")); MenuItem hideImages = new MenuItem("Hide Images"); hideImages.setOnAction(e -> System.out.println("Hide Images...")); contextMenu.getItems().addAll(reload, savePage, hideImages); webView.setOnMousePressed(e -> { if (e.getButton() == MouseButton.SECONDARY) { contextMenu.show(webView, e.getScreenX(), e.getScreenY()); } else { contextMenu.hide(); } }); } private String getUrl(String text) { if (text.indexOf("://")==-1) { return "http://" + text ; } else { return text ; } } public static void main(String[] args) { launch(args); } } 

对此没有简单的解决方案,因为没有公共API,并且请求仍未解决。

hacky解决方案使用了一些私有API,所以它不太合适,因为它可能会在没有通知的情况下发生变化。

当用户右键单击网页时显示的ContextMenu在另一个窗口中,因此使用一些查找我们将尝试找到它,然后访问其内容然后修改现有或添加更多MenuItem

这些是所需的私人课程:

 import com.sun.javafx.scene.control.skin.ContextMenuContent; import com.sun.javafx.scene.control.skin.ContextMenuContent.MenuItemContainer; 

在我们的应用程序中,我们监听上下文菜单请求:

 @Override public void start(Stage primaryStage) { WebView webView = new WebView(); WebEngine webEngine = webView.getEngine(); Scene scene = new Scene(webView); primaryStage.setScene(scene); primaryStage.show(); webView.setOnContextMenuRequested(new EventHandler() { @Override public void handle(ContextMenuEvent e) { getPopupWindow(); } }); } 

getPopupWindow()将在哪里:

  • 寻找新窗口作为ContextMenu实例
  • 使用lookup查找CSS选择器context-menu 。 这是一个节点,它的唯一子节点是ContextMenuContent实例。
  • 这个对象有一个VBox作为所有项目的容器,它们是一个特殊容器中的MenuItemContainerMenuItemContainer
  • 我们可以访问任何现有项目,例如重新加载页面,返回,…并自定义它们,修改其文本或添加图形。
  • 我们可以将自定义项添加到此框中,提供我们自己的操作。

根据需要自定义项目:

 private PopupWindow getPopupWindow() { @SuppressWarnings("deprecation") final Iterator windows = Window.impl_getWindows(); while (windows.hasNext()) { final Window window = windows.next(); if (window instanceof ContextMenu) { if(window.getScene()!=null && window.getScene().getRoot()!=null){ Parent root = window.getScene().getRoot(); // access to context menu content if(root.getChildrenUnmodifiable().size()>0){ Node popup = root.getChildrenUnmodifiable().get(0); if(popup.lookup(".context-menu")!=null){ Node bridge = popup.lookup(".context-menu"); ContextMenuContent cmc= (ContextMenuContent)((Parent)bridge).getChildrenUnmodifiable().get(0); VBox itemsContainer = cmc.getItemsContainer(); for(Node n: itemsContainer.getChildren()){ MenuItemContainer item=(MenuItemContainer)n; // customize text: item.getItem().setText("My Custom: "+item.getItem().getText()); // customize graphic: item.getItem().setGraphic(new ImageView(new Image(getClass().getResource("unlock24.png").toExternalForm()))); } // remove some item: // itemsContainer.getChildren().remove(0); // adding new item: MenuItem menuItem = new MenuItem("Save page"); menuItem.setOnAction(new EventHandler() { @Override public void handle(ActionEvent e) { System.out.println("Save Page"); } }); // add new item: cmc.getItemsContainer().getChildren().add(cmc.new MenuItemContainer(menuItem)); return (PopupWindow)window; } } } return null; } } return null; } 

这是它的样子:

自定义ContextMenu