也许有人可以解释以下行为 - 希望,可能的解决方法...谢谢。
当使用含文本字段一个CustomMenuItem,该菜单项的作用得到上面按文本框进入里面......除非该文本框有一个ActionListener组(无添加)...我需要使用的addEventHandler,不setOnAction引发... : - /
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
public class CustomMenuTest extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
MenuButton menu = new MenuButton("Fancy Menu...");
MenuItem hello = new MenuItem("Hello");
hello.setOnAction(event -> System.out.println("Hello | " + event.getSource()));
MenuItem world = new MenuItem("World!");
world.setOnAction(event -> System.out.println("World! | " + event.getSource()));
/*
Set the cursor into the TextField, maybe type something, and hit enter.
--> Expected: "ADD: <Text you typed> ..."
--> Actual: "ADD: <Text you typed> ..." AND "World! ..." - so the button above gets triggered as well.
If I activate listener (II) or (III), everything works fine - even the empty action in (III) does is job, but this is ugly...
(And I can't use (II), because I need (I).
*/
TextField textField = new TextField();
/* I */ textField.addEventHandler(ActionEvent.ACTION,
event -> System.out.println("ADD: " + textField.getText() + " | " + event.getSource()));
/* II */ // textField.setOnAction(event -> System.out.println("SET: " + textField.getText() + " | " + event.getSource()));
/* III */ // textField.setOnAction(__ -> {/* do nothing */});
CustomMenuItem custom = new CustomMenuItem(textField, false);
menu.getItems().addAll(hello, world, custom);
primaryStage.setScene(new Scene(menu));
primaryStage.show();
}
}
我使用Java 8。
有任何想法吗?
原因(至少部分)是ContextMenuContent的内部(充当皮肤种类,为的菜单项列表)感到困惑:
- 它注册上输入(在结束时)一个keyHandler触发内部跟踪当前聚焦项目
- 内部跟踪不上点击进入自定义内容正常工作
一个黑客周围是强制的内部状态的更新(注意:需要访问隐藏API),网络连接的文本框的处理程序的mouseEntered。
一些代码:
TextField textField = new TextField();
CustomMenuItem custom = new CustomMenuItem(textField, false);
// menuItemContainer registers a mouseClicked handler that fires
// need to consume before it reaches the container
textField.addEventFilter(MouseEvent.MOUSE_CLICKED, e -> e.consume());
// hack to update internal state of ContextMenuItem
textField.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> {
ContextMenuContent cmContent = null;
Node node = textField;
while (node != null) {
node = node.getParent();
if (node instanceof ContextMenuContent) {
cmContent = (ContextMenuContent) node;
break;
}
}
if (cmContent != null) {
Parent menuItemContainer = textField.getParent();
Parent menuBox = menuItemContainer.getParent();
int index = menuBox.getChildrenUnmodifiable().indexOf(menuItemContainer);
// hack internal state
cmContent.requestFocusOnIndex(index);
}
});
/* I */
textField.addEventHandler(ActionEvent.ACTION, event -> {
System.out.println("ADD: " + textField.getText() + " | "
+ event.getSource()
);
// consume to prevent item to fire twice
event.consume();
});
custom.setOnAction(e -> {
// someone needs to hide the popup
// could be done in textField actionHandler as well
if (custom.getParentPopup() != null) {
custom.getParentPopup().hide();
}
System.out.println("action custom menu " + e.getSource());
});
该错误报道。
在进一步深挖:实际的罪魁祸首似乎是MenuItemContainer(即对单个项目的容器)
- 对于customMenuItems它注册了本身请求焦点处理器的mouseEntered
- 对于其它类型的的菜单项它注册在其focusedProperty收听者,其更新ContextMenuContent的currentFocusedIndex
一个干净的修复可能是注册的焦点侦听器的所有项目(除分隔符)
挖远一点,打开了另一种选择/错误)的原因setOnAction VS addHandler操作(动作事件......)的不同的行为是在TextFieldBehaviour一些腥代码:
@Override protected void fire(KeyEvent event) {
TextField textField = getNode();
EventHandler<ActionEvent> onAction = textField.getOnAction();
ActionEvent actionEvent = new ActionEvent(textField, null);
textField.commitValue();
textField.fireEvent(actionEvent);
// ---> this condition smells
if (onAction == null && !actionEvent.isConsumed()) {
forwardToParent(event);
}
}
我认为,执行意图是只转发无论是否有特殊的OnAction处理或没有正常的处理消耗了它。 检查所消耗的事件总是返回false,即使处理程序都有它消耗 - 因为该事件被分派过程中复制......这是处理程序改变消耗的一个副本,而不是原件。
转发到父火灾不正确的菜单项(由于内部簿记错误,见上文),所以在寻找另一种方式来防止它:
protected void forwardToParent(KeyEvent event) {
// fix for JDK-8145515
if (getNode().getProperties().containsKey(DISABLE_FORWARD_TO_PARENT)) {
return;
}
if (getNode().getParent() != null) {
getNode().getParent().fireEvent(event);
}
}
而事实上,并称标记属性防止发射了错误的项目 - 无需访问内部类(虽然仍高度依赖于实现的。):
textField.getProperties().put(
"TextInputControlBehavior.disableForwardToParent", true);
textField.addEventHandler(ActionEvent.ACTION, event -> {