JavaFX MenuItem可以通过设置ActionEvent EventHandler来响应大多数KeyPress事件。但是,虽然事件处理程序确实捕获了KeyCode.ENTER的KeyPress,但它不捕获KeyCode.TAB KeyPress事件。显然,像TAB这样的一些关键事件是在更深层次上处理的。例如,箭头键可以遍历菜单。
我的ContextMenu是用户已开始在TextField中输入的电子邮件地址字符串的完成列表。用户想要按箭头键选择所需的项目,然后按TAB键执行完成。
我可以将事件处理程序附加到ContextMenu本身并捕获TAB按键。但事件的Source是ContextMenu,我在ContextMenu中找不到任何变量,指示在按下TAB键时哪个MenuItem突出显示。 MenuItem允许css样式控制焦点中菜单项的外观,但它没有任何属性来判断它是否处于焦点。
我通过MenuItem buildEventDispatchChain()尝试使用EventDispatchChain,但无济于事。在按下TAB键时,似乎没有办法拦截TAB KeyPress或以其他方式确定哪个菜单项处于焦点。
有什么建议吗?
答案 0 :(得分:0)
如果我说得对,你想要覆盖默认的按键式监听器来添加你自己的响应,所以我们必须找到它应用的位置。
为了实现这一目标,我们必须亲自动手使用私有API ...
ContextMenu
skin(ContextMenuSkin
)使用ContextMenuContent
对象作为包含所有项目的容器。这些项目中的每一项都位于ContextMenuContent.MenuItemContainer
容器中。
我们可以覆盖父容器上的keypressed侦听器,同时我们可以将itemsProperty侦听器添加到items容器上的项目。
使用此私有API
import com.sun.javafx.scene.control.skin.ContextMenuContent;
这对我有用:
private ContextMenuContent.MenuItemContainer itemSelected=null;
@Override
public void start(Stage primaryStage) {
MenuItem cmItem1 = new MenuItem("Item 1");
cmItem1.setOnAction(e->System.out.println("Item 1"));
MenuItem cmItem2 = new MenuItem("Item 2");
cmItem2.setOnAction(e->System.out.println("Item 2"));
final ContextMenu cm = new ContextMenu(cmItem1,cmItem2);
Scene scene = new Scene(new StackPane(), 300, 250);
scene.setOnMouseClicked(t -> {
if(t.getButton()==MouseButton.SECONDARY || t.isControlDown()){
cm.show(scene.getWindow(),t.getScreenX(),t.getScreenY());
ContextMenuContent cmc= (ContextMenuContent)cm.getSkin().getNode();
cmc.setOnKeyPressed(ke->{
switch (ke.getCode()) {
case UP: break;
case DOWN: break;
case TAB: ke.consume();
if(itemSelected!=null){
itemSelected.getItem().fire();
}
cm.hide();
break;
default: break;
}
});
VBox itemsContainer = cmc.getItemsContainer();
itemsContainer.getChildren().forEach(n->{
ContextMenuContent.MenuItemContainer item=(ContextMenuContent.MenuItemContainer)n;
item.focusedProperty().addListener((obs,b,b1)->{
if(b1){
itemSelected=item;
}
});
});
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
答案 1 :(得分:0)
出色!谢谢@jose!我最后编写了一些不同的代码但是 关键是使用com.sun.javafx.scene.control.skin.ContextMenuContent,它提供了 访问包含MenuItems的ContextMenuContent.MenuItemContainer对象。
为了不破坏现有的UP / DOWN键行为,我添加了一个新的处理程序 到ContextMenuContent对象;这个处理程序只使用TAB KeyPress和 everthing else传递给他们的正常处理程序。
查看ContextMenuContent类,我借用了他们现有的方法 找到焦点项,所以没有必要添加focusedProperty听众。
另外,我使用的是Java 1.7并且没有lambda,我使用的是非常基本的编程风格。
public class MenuItemHandler_CMC <T extends Event> implements EventHandler {
public ContextMenuContent m_cmc;
public AddressCompletionMenuItemHandler_CMC(ContextMenuContent cmc){
m_cmc = cmc;
}
@Override
public void handle(Event event){
KeyEvent ke = (KeyEvent)event;
switch(ke.getCode()){
case TAB:
ke.consume();
MenuItem focused_menu_item = findFocusedMenuItem();
if(focused_menu_item != null){
focused_menu_item.fire();
}
break;
default: break;
}
}
public MenuItem findFocusedMenuItem() {
VBox items_container = m_cmc.getItemsContainer();
for (int i = 0; i < items_container.getChildren().size(); i++) {
Node n = items_container.getChildren().get(i);
if (n.isFocused()) {
ContextMenuContent.MenuItemContainer menu_item_container = (ContextMenuContent.MenuItemContainer)n;
MenuItem menu_item = menu_item_container.getItem();
return menu_item;
}
}
return null;
}
}
...附加额外的处理程序
if(m_context_menu.getSkin() != null){
ContextMenuContent cmc = (ContextMenuContent)m_context_menu.getSkin().getNode();
MenuItemHandler_CMC menu_item_handler_cmc = new MenuItemHandler_CMC(cmc);
cmc.addEventHandler(KeyEvent.KEY_PRESSED, menu_item_handler_cmc);
}