我有一个扩展CustomMenuItem的类。此MenuItems被添加到ContextMenu。现在我需要从CustomMenuItem的右侧获取X坐标。
问题是,我不知道如何获得坐标。 CustMenuItem没有获取坐标的功能,如getX()或getY()。
那么我该如何解决这个问题呢?
这件事我想得到:
在这里,我们可以看到上下文菜单的示例(红线)。在Context菜单中实现了很多不同的CustomMenuItems。现在我想获得CustomMenuItem的右上角坐标。
感谢您的帮助。
答案 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;
}
如果删除效果:
.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;
}
这就是它的样子:
因此,自定义上下文菜单上每个项目的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");
}
};