JavaFX TextArea 中的 Tab 键导航
Posted
技术标签:
【中文标题】JavaFX TextArea 中的 Tab 键导航【英文标题】:Tab key navigation in JavaFX TextArea 【发布时间】:2012-10-03 08:27:26 【问题描述】:如何让 TextArea 中的 Tab 键导航到下一个控件?
我可以为按键按下事件添加一个侦听器,但是如何使 TextArea 控件失去焦点(不知道要聚焦的链中的下一个字段)?
@FXML protected void handleTabKeyTextArea(KeyEvent event)
if (event.getCode() == KeyCode.TAB)
...
【问题讨论】:
【参考方案1】:如果按 TAB,则此代码遍历焦点,如果按 CONTROL+TAB,则插入标签
textArea.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>()
@Override
public void handle(KeyEvent event)
if (event.getCode() == KeyCode.TAB)
SkinBase skin = (SkinBase) textArea.getSkin();
if (skin.getBehavior() instanceof TextAreaBehavior)
TextAreaBehavior behavior = (TextAreaBehavior) skin.getBehavior();
if (event.isControlDown())
behavior.callAction("InsertTab");
else
behavior.callAction("TraverseNext");
event.consume();
);
【讨论】:
一个小问题:它应该检查 event.isShiftDown() 应该调用“TraversePrevious”,而不是“TraverseNext”。 至少对于 JavaFX 8,SkinBase 应该改为 TextAreaSkin。【参考方案2】:我使用遍历方法
@Override
public void handle(KeyEvent event)
if (event.getCode().equals(KeyCode.TAB))
Node node = (Node) event.getSource();
if (node instanceof TextField)
TextFieldSkin skin = (TextFieldSkin) ((TextField)node).getSkin();
if (event.isShiftDown())
skin.getBehavior().traversePrevious();
else
skin.getBehavior().traverseNext();
else if (node instanceof TextArea)
TextAreaSkin skin = (TextAreaSkin) ((TextArea)node).getSkin();
if (event.isShiftDown())
skin.getBehavior().traversePrevious();
else
skin.getBehavior().traverseNext();
event.consume();
【讨论】:
这个解决方案是我发现的最干净的。虽然没有必要为 TextFields 定义它,因为这已经是默认设置(至少在 Java 8 中)。 但问题是,它会强制您扩展 TextArea - 如果您使用 Scene Builder,则不是很方便。 ...实际上@Override 让我失望了,TextArea 中没有要覆盖的句柄方法,所以我想这只是一个常规的处理程序方法。不过,我希望有一个更简单的方法 - 哦,好吧。 ...实际上,有一种方法可以让它更容易重复使用!将一个专门用于此的静态类事件处理程序放在公共的地方,并在 TextArea 上调用它: .addEventFilter(KeyEvent.KEY_PRESSED, new UsabilityUtil.TabTraversalEventHandler()); TextAreaSkin 在 Java 1.8.0_311-b11 中无法解析,但 MarcG 的解决方案运行良好。【参考方案3】:我遇到了同样的问题,我喜欢 Tom 使用的遍历方法。 但我也想在按下 ctrl+tab 时插入一个选项卡。
电话
behavior.callAction("InsertTab");
不适用于 JavaFX8。查看 TextAreaBehaviour 类,我发现现在有一个“TraverseOrInsertTab”动作。
但是,我认为这种动作调用在多个 java 版本中非常不稳定,因为它依赖于传递的字符串。
所以我没有使用 callAction() 方法,而是使用了
textArea.replaceSelection("\t");
【讨论】:
【参考方案4】:如果选项卡 - 焦点问题的不同解决方案。 CTRL+TAB 键的 TextArea 的默认行为是将焦点移动到下一个控件。所以我用 CTRL+TAB 键事件替换了 TAB 键事件,当用户按下 CTRL+TAB 时,TextArea 中会插入一个制表符。
我的问题:可以在事件过滤器中触发事件吗?是否可以将 KeyEvent 的文本替换为 FOCUS_EVENT_TEXT,以便指示它是用户生成的事件,还是来自事件过滤器中创建的事件。
事件过滤器:
javafx.scene.control.TextArea textArea1 = new javafx.scene.control.TextArea();
textArea1.addEventFilter(KeyEvent.KEY_PRESSED, new TextAreaTabToFocusEventHandler());
事件处理程序:
public class TextAreaTabToFocusEventHandler implements EventHandler<KeyEvent>
private static final String FOCUS_EVENT_TEXT = "TAB_TO_FOCUS_EVENT";
@Override
public void handle(final KeyEvent event)
if (!KeyCode.TAB.equals(event.getCode()))
return;
// handle events where the TAB key or TAB + CTRL key is pressed
// so don't handle the event if the ALT, SHIFT or any other modifier key is pressed
if (event.isAltDown() || event.isMetaDown() || event.isShiftDown())
return;
if (!(event.getSource() instanceof TextArea))
return;
final TextArea textArea = (TextArea) event.getSource();
if (event.isControlDown())
// if the event text contains the special focus event text
// => do not consume the event, and let the default behaviour (= move focus to the next control) happen.
//
// if the focus event text is not present, then the user has pressed CTRL + TAB key,
// then consume the event and insert or replace selection with tab character
if (!FOCUS_EVENT_TEXT.equalsIgnoreCase(event.getText()))
event.consume();
textArea.replaceSelection("\t");
else
// The default behaviour of the TextArea for the CTRL+TAB key is a move of focus to the next control.
// So we consume the TAB key event, and fire a new event with the CTRL + TAB key.
event.consume();
final KeyEvent tabControlEvent = new KeyEvent(event.getSource(), event.getTarget(), event.getEventType(), event.getCharacter(),
FOCUS_EVENT_TEXT, event.getCode(), event.isShiftDown(), true, event.isAltDown(), event.isMetaDown());
textArea.fireEvent(tabControlEvent);
【讨论】:
【参考方案5】:受先前答案的启发,对于一个非常相似的案例,我构建了以下类:
/**
* Handles tab/shift-tab keystrokes to navigate to other fields,
* ctrl-tab to insert a tab character in the text area.
*/
public class TabTraversalEventHandler implements EventHandler<KeyEvent>
@Override
public void handle(KeyEvent event)
if (event.getCode().equals(KeyCode.TAB))
Node node = (Node) event.getSource();
if (node instanceof TextArea)
TextAreaSkin skin = (TextAreaSkin) ((TextArea)node).getSkin();
if (!event.isControlDown())
// Tab or shift-tab => navigational action
if (event.isShiftDown())
skin.getBehavior().traversePrevious();
else
skin.getBehavior().traverseNext();
else
// Ctrl-Tab => insert a tab character in the text area
TextArea textArea = (TextArea) node;
textArea.replaceSelection("\t");
event.consume();
我只是没有看到在 TextField 的上下文中处理选项卡的必要性,所以我删除了这部分。
那么这个类就可以如User所描述的那样非常容易地使用:
TextArea myTextArea = new TextArea();
mytTextArea.addEventFilter(KeyEvent.KEY_PRESSED, new TabTraversalEventHandler());
整个事情就像一个魅力:)
【讨论】:
【参考方案6】:从 Java 9 (2017) 开始,此页面中的大多数答案都不起作用,因为您不能再使用 skin.getBehavior()
。
这行得通:
@Override
public void handle(KeyEvent event)
KeyCode code = event.getCode();
if (code == KeyCode.TAB && !event.isShiftDown() && !event.isControlDown())
event.consume();
Node node = (Node) event.getSource();
try
Robot robot = new Robot();
robot.keyPress(KeyCode.CONTROL.getCode());
robot.keyPress(KeyCode.TAB.getCode());
robot.delay(10);
robot.keyRelease(KeyCode.TAB.getCode());
robot.keyRelease(KeyCode.CONTROL.getCode());
catch (AWTException e)
这也有效:
@Override
public void handle(KeyEvent event)
KeyCode code = event.getCode();
if (code == KeyCode.TAB && !event.isShiftDown() && !event.isControlDown())
event.consume();
Node node = (Node) event.getSource();
KeyEvent newEvent
= new KeyEvent(event.getSource(),
event.getTarget(), event.getEventType(),
event.getCharacter(), event.getText(),
event.getCode(), event.isShiftDown(),
true, event.isAltDown(),
event.isMetaDown());
node.fireEvent(newEvent);
当用户按下TAB
时,两者都模拟按下CTRL+TAB
。 CTRL+TAB
的 TextArea 的默认行为是将焦点移动到下一个控件。请注意,第二个代码基于 Johan De Schutter 的回答。
【讨论】:
此解决方案也适用于 Java 1.8.0_311-b11。谢谢大佬。【参考方案7】:JavaFX 16(以及 17)中的 TextAreaSkin 类没有 getBehavior() 方法。为了防止在 TextArea 中插入标签,TextAreaTabToFocusEventHandler 的解决方案适用于我
【讨论】:
访问该行为总是脏的——如果我们被允许,我们仍然可以在其 inputMap 中以类似的脏度和tweak the keyMappings 访问它以上是关于JavaFX TextArea 中的 Tab 键导航的主要内容,如果未能解决你的问题,请参考以下文章
如何禁用 TextArea (JavaFX) 中的文本选择?
Javafx 8 替换 textarea 中的文本并保持格式
Javafx 不接受文本字段的 textArea 中的其他字体或语言