如何正确监听键盘事件
问题
对于一个事件的触发,这里摘取JavaFx China对于常用的鼠标事件和键盘事件的描述
当一个动作发生时,系统根据内部规则决定哪一个Node是事件目标。规则如下:
● 对于键盘事件,事件目标是已获取焦点的Node。
● 对于鼠标事件,事件目标是光标所在位置处的Node。
对于一个可输入可聚焦的类似于textField的对象,键盘事件的使用也完全没有任何问题,因为用的是ActionEvent,不再赘述。
但倘若有如下场景,需要为一个不可聚焦的circle注册一个Y快捷键,让你单击Y键使得circle半径增大一倍,你可能会编写如下代码
Circle circle = new Circle(100,100,50);
Pane pane = new Pane();
ObservableList<Node> list = pane.getChildren();
list.add(circle);
//注册快捷键明显用lambda表达式创建匿名对象更合适
circle.setOnKeyTyped(e->{
if(e.getCode() == KeyCode.Y)
circle.setRadius(circle.getRadius()*2);
});
运行后界面如下
你会发现如何按Y键这个圆也没有变大
分析
你通过查询API文档中对setOnKeyType的描述,
Defines a function to be called when this Node
or its child Node
has input focus and a key has been typed. The function is called only if the event hasn't been already consumed during its capturing or bubbling phase.
你会留意到红色注明的地方,猜想事件无响应的原因就是没有focus,对circle调用输出isFocused()结果为false,证实了我们的猜想
System.out.println(circle.isFocused())
机智的你可能会查到使用requestFocus来手动让circle获取焦点,从而可以正常监听键盘事件,但结果……
circle.requestFocus();
System.out.println(circle.isFocused())
纳尼!明明上一句代码requestFocus紧接着输出怎么还是false,md一定是requestFocus没用,这JavaFX什么鬼!
并非requestFocus无用,而是JavaFX的另一个机制在作怪(具体测试方法就是不断变换上述代码的位置):焦点只能在stage.show()了之后才能获取,而且默认由stage.setScene(scene)中的scene获取。所以circle.requestFocus()要在scene.setOnKeyType中进行才有用
结论
1.对于快键键这类需要全局监听的键盘事件,对scene进行setOnKeyType,然后根据e.getCode的值来调用相应的方法
2.对于确实需要让该子节点获得焦点的情况,需要其他可顺利触发的事件的处理器中node.requestFocus()来开启这个node子节点的键盘监听