JavaFX CustomMenuItem的右侧坐标

时间:2014-11-07 10:14:41

标签: javafx custom-controls coordinates

我有一个扩展CustomMenuItem的类。此MenuItems被添加到ContextMenu。现在我需要从CustomMenuItem的右侧获取X坐标。

问题是,我不知道如何获得坐标。 CustMenuItem没有获取坐标的功能,如getX()或getY()。

那么我该如何解决这个问题呢?

这件事我想得到:

enter image description here

在这里,我们可以看到上下文菜单的示例(红线)。在Context菜单中实现了很多不同的CustomMenuItems。现在我想获得CustomMenuItem的右上角坐标。

感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

在处理菜单项之前,让我们开始说ContextMenu是一个弹出窗口,因此它具有Window属性。你可以要求(x,y)左,顶部和(w,h)。

但是你必须考虑到这些效果,因为默认情况下它包含一个阴影。当它发生时,在右侧和底部增加了24x24像素的额外空间。

.context-menu {
    -fx-effect: dropshadow( gaussian , rgba(0,0,0,0.2) , 12, 0.0 , 0 , 8 );
}

由于此默认阴影的半径为12px,Y偏移为8px的底部,因此上下文菜单的右下坐标(包括24x24区域)由下式给出:

X=t.getX()+cm.getWidth()-12-24;
Y=t.getY()+cm.getHeight()-(12-8)-24;

其中t可以是相对于场景的MouseEvent,并且为了简单起见,值是硬编码的。

让我们看一个例子。由于您没有说明自定义菜单项的实现方式,因此我将创建一个包含图形和文本的简单菜单项:

private final Label labX = new Label("X: ");
private final Label labY = new Label("Y: ");

@Override
public void start(Stage primaryStage) {
    final ContextMenu cm = new ContextMenu();
    MenuItem cmItem1 = createMenuItem("mNext", "Next Long Option",t->System.out.println("next"));
    MenuItem cmItem2 = createMenuItem("mBack", "Go Back", t->System.out.println("back"));

    SeparatorMenuItem sm = new SeparatorMenuItem();
    cm.getItems().addAll(cmItem1,cmItem2);

    VBox root = new VBox(10,labX,labY);
    Scene scene = new Scene(root, 300, 250);
    scene.setOnMouseClicked(t->{
        if(t.getButton()==MouseButton.SECONDARY || t.isControlDown()){ 
            // t.getX,Y->scene based coordinates
            cm.show(scene.getWindow(),t.getX()+scene.getWindow().getX()+scene.getX(), 
                                      t.getY()+scene.getWindow().getY()+scene.getY());
            labX.setText("Right X: "+(t.getX()+cm.getWidth()-12-24));
            labY.setText("Bottom Y: "+(t.getY()+cm.getHeight()-4-24));
        }
    });
    scene.getStylesheets().add(getClass().getResource("root.css").toExternalForm());
    primaryStage.setScene(scene);
    primaryStage.show();

    primaryStage.setTitle("Scene: "+scene.getWidth()+"x"+scene.getHeight());
}

private MenuItem createMenuItem(String symbol, String text, EventHandler<ActionEvent> t){
    MenuItem m=new MenuItem(text);
    StackPane g=new StackPane();
    g.setPrefSize(24, 24);
    g.setId(symbol);
    m.setGraphic(g);
    m.setOnAction(t);
    return m;
}

Custom context menu with effect

如果删除效果:

.context-menu {
    -fx-effect: null;
}

然后这些坐标是:

X=t.getX()+cm.getWidth();
Y=t.getY()+cm.getHeight();

现在我们有了窗口,让我们进入项目。

MenuItem皮肤来自(私有)ContextMenuContent.MenuItemContainer类,它是Region,其中图形和文本都是布局的。

构建上下文菜单时,所有项目都包含在VBox中,并且所有项目都同样调整大小,您可以看到是否设置了项目的边框:

.menu-item {
    -fx-border-color: black;
    -fx-border-width: 1;
}

这就是它的样子:

Custom context menu with borde

因此,自定义上下文菜单上每个项目的X坐标与其父项的X相同(参见上文,有效或无效),减去1个填充像素(默认情况下)。

请注意,您也可以通过私有方法获取商品的尺寸:

ContextMenuContent cmc= (ContextMenuContent)cm.getSkin().getNode();
System.out.println("cmc: "+cmc.getItemsContainer().getBoundsInParent());

虽然不建议这样做,因为私有API将来可能会发生变化。

修改

根据请求,这是删除lambdas和css的相同代码。

private final Label labX = new Label("X: ");
private final Label labY = new Label("Y: ");

@Override
public void start(Stage primaryStage) {
    final ContextMenu cm = new ContextMenu();
    MenuItem cmItem1 = createMenuItem("mNext", "Next Long Option",action);
    MenuItem cmItem2 = createMenuItem("mBack", "Go Back", action);
    SeparatorMenuItem sm = new SeparatorMenuItem();
    cm.getItems().addAll(cmItem1,cmItem2);

    VBox root = new VBox(10,labX,labY);
    Scene scene = new Scene(root, 300, 250);
    scene.setOnMouseClicked(new EventHandler<MouseEvent>() {

        @Override
        public void handle(MouseEvent t) {
            if(t.getButton()==MouseButton.SECONDARY || t.isControlDown()){
                // t.getX,Y->scene based coordinates
                cm.show(scene.getWindow(),t.getX()+scene.getWindow().getX()+scene.getX(),
                        t.getY()+scene.getWindow().getY()+scene.getY());
                labX.setText("Right X: "+(t.getX()+cm.getWidth()-12-24));
                labY.setText("Bottom Y: "+(t.getY()+cm.getHeight()-4-24));
            }
        }
    });
    primaryStage.setScene(scene);
    primaryStage.show();

    primaryStage.setTitle("Scene: "+scene.getWidth()+"x"+scene.getHeight());
}

private MenuItem createMenuItem(String symbol, String text, EventHandler<ActionEvent> t){
    MenuItem m=new MenuItem(text);
    StackPane g=new StackPane();
    g.setPrefSize(24, 24);
    g.setId(symbol);
    SVGPath svg = new SVGPath();
    svg.setContent("M0,5H2L4,8L8,0H10L5,10H3Z");
    m.setGraphic(svg);
    m.setOnAction(t);
    return m;
}

private final EventHandler<ActionEvent> action = new EventHandler<ActionEvent>() {

    @Override
    public void handle(ActionEvent event) {
        System.out.println("action");
    }
};