【问题标题】:Customized context menu on javafx webview/webenginejavafx webview/webengine上的自定义上下文菜单
【发布时间】:2015-01-18 18:39:48
【问题描述】:

如何为WebEngine javafx 中的整个文档条目提供自定义上下文菜单?
像这样的

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

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

【问题讨论】:

    标签: java javafx


    【解决方案1】:

    没有简单的解决方案,因为没有公共 API,request 仍未解决。

    hacky 解决方案使用了一些私有 API,因此不太可取,因为它可能会在没有通知的情况下更改。

    当用户在网页上右击时显示的ContextMenu在另一个窗口中,所以我们会通过一些查找来尝试找到它,然后访问它的内容,然后修改现有的或添加更多MenuItems .

    这些是所需的私有类:

    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<ContextMenuEvent>() {
    
            @Override
            public void handle(ContextMenuEvent e) {
                getPopupWindow();
            }
        });
    
    }
    

    getPopupWindow() 将在哪里:

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

    根据需要自定义项目:

    private PopupWindow getPopupWindow() {
        @SuppressWarnings("deprecation") 
        final Iterator<Window> 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<ActionEvent>() {
    
                                @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;
    }
    

    看起来是这样的:

    【讨论】:

    • getOnAction 显示为 null,getProperties 为 {},如何获取事件处理函数
    【解决方案2】:

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

    使用

    禁用默认上下文菜单
    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);
        }
    }
    

    【讨论】:

    • 完美先生,如何在鼠标左键单击事件后隐藏上下文菜单?它也不会随着文档滚动而滚动。
    • 我添加了一个更新,以在用户单击而不是右键单击时隐藏菜单。滚动有点棘手...不确定,因为没有明显的方法来跟踪滚动值...
    • 完美先生,我还添加了一个滚动事件来隐藏弹出窗口,它就像魅力一样工作。谢谢先生。
    猜你喜欢
    • 2016-11-18
    • 2013-10-09
    • 1970-01-01
    • 1970-01-01
    • 2011-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多