如何在JComboBox中检测TAB键的按下?

3

我正在尝试找出如何检测JComboBox失去焦点的方法,以及用户是通过按Tab键还是通过单击组件区域外来进行操作。

将FocusListener添加到JComboBox的编辑器组件中对我没有帮助,因为我无法确定用户是使用鼠标还是通过Tab键移动了焦点。非常感谢任何想法。

编辑1: 我的目标是:

  • 假设用户放下列表(JComboBox弹出菜单显示),并通过光标键导航...
  • 情况1:用户按Tab键。在这种情况下,我想从项目中截取一些信息并仅显示一些部分。
  • 情况2:用户在popupMenu区域之外用鼠标单击(这里有子情况,但它们都属于同一类别)。在这种情况下,我想更改JComboBox以显示之前编辑的项目,而不是用户导航的内容...
  • 还有几种情况(鼠标选择项目、回车键、Esc等)。我可以轻松处理这些内容,但是检测Tab键很棘手,因为我无法捕获此事件,因为它由FocusManager处理。

编辑2: 似乎我必须使用setFocusTraversalKeysEnabled(false)来在按TAB时得到通知,当我捕获到该事件时,我应该手动转移焦点...我不喜欢这种解决方案,但这是我能想出的最好的解决方案。

解决方案:

以下Java代码实际上解决了我的问题。正如我在编辑2中所写的那样,最简单的解决方案是禁用焦点遍历。我无耻地借用了Kleopatra的代码,现在一切都正常了。 :)

    if (!isTableCellEditor()) {
        comboBoxEditor.setFocusTraversalKeysEnabled(false);

        Action myAction = new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent e) {
                handleTabPress();
                comboBoxEditor.transferFocus();
            } // actionPerformed() method
        };

        comboBoxEditor.getActionMap().put("tab-action", myAction);
        comboBoxEditor.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
            .put(KeyStroke.getKeyStroke("TAB"), "tab-action");
    } // if

感谢参与讨论的所有人!

"任何想法都将不胜感激。" 这种功能的使用情景是在请求帮助或建议时,表达谦恭和感激之意。通过了解用户需要什么功能或解决方案,可以提供更好的产品或服务。 - Andrew Thompson
Andrew,感谢您的回复,请查看Edit 1 - DejanLekic
2个回答

6

我理解你的问题有两个独立的问题

  • 在导航时不提交值
  • 接管TAB键的反应

如果是这样,答案如下:

  • 将组合框配置为它认为自己是 CellEditor 的编辑组件
  • 禁用默认的遍历键并使用自定义绑定接管

代码如下:

    final JComboBox simpleBox = new JComboBox(Locale.getAvailableLocales());
    // this line configures the combo to only commit on ENTER 
    // or selecting an item from the list
    simpleBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
    //
    // simpleBox.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
    //     Collections.EMPTY_SET);
    // just noticed the OPs edit - following indeed is easier to disable _all_ traversal
    // keys with one statement
    simpleBox.setFocusTraversalKeysEnabled(false);

    Action myAction = new AbstractAction() {

        @Override
        public void actionPerformed(ActionEvent e) {
            LOG.info("got it!");
            simpleBox.transferFocus();
        }

    };
    simpleBox.getActionMap().put("tab-action", myAction);
    simpleBox.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
       .put(KeyStroke.getKeyStroke("TAB"), "tab-action");

谢谢Kleopatra,终于有人理解这个问题了...当我的组合框在一个表单中(而不是单元格编辑器)时,我什么也不提交。一切都是自动的。正如您可能知道的那样,当您导航时,会自动调用setSelectedItem()。所以,我所做的是-当用户按ENTER键或使用鼠标选择项目时,我会存储它,以便我知道实际选择了哪个项目。(这相当于非提交,因为我总是显示最新的PICKED项目)。我不能将其设置为认为它是单元格编辑器,因为当它是单元格编辑器时,我有单独的逻辑... :( - DejanLekic
如果您查看我的编辑2,您会看到我现在正在尝试做什么...那似乎是最简单的解决方案。您觉得呢? - DejanLekic
可能不太明白你的需求;-) 设置客户端属性可以使组合框按照你描述的要求进行操作,我认为:用户可以在不提交(即:选择到组合框)下拉列表中选定的项目的情况下导航。如果他/她想提交,他/她必须主动这样做-通过按ENTER键或通过鼠标单击下拉菜单。对于“点击其他地方”的情况,没有其他需要编写的代码。 - kleopatra
如果我没有针对当我的组合框是单元格编辑器或不是时的单独逻辑,那么JTable.isCellEditor客户端属性将完全符合我的要求。但不幸的是,这会破坏我在它不是单元格编辑器时的逻辑... :( 所以我要么重构它,要么禁用焦点遍历并捕获TAB键... - DejanLekic
重构 - 因为对我来说,它听起来像你走错了路,瞎猜的,当然,没有看到任何细节 :-) 你怎么样用一个 sscce 来描述你需要什么以及为什么你认为你不能使用上面的方法呢? - kleopatra

3
假设你在失去焦点之前按下了鼠标(或键盘)。因此,请听取所有键盘和鼠标点击声,最后一次使用的右键是罪魁祸首。
当触发focuslost事件时,您需要检查一个变量,该变量由窗口的任何部分设置为true当鼠标或键按下时。这个变量仅记录最后一次按下的鼠标和/或键。当然,您必须捕获所有的鼠标和键输入,因为单击任何小部件都会让您失去焦点。

无论如何都会触发FocusLost,我无法确定它是在用户按下TAB还是点击其他地方时被触发...这就是问题的实质。 - DejanLekic
是的,当焦点丢失时,您将检查在窗口的任何部分按下鼠标或键时设置的变量。此变量仅记录最后一次按下的鼠标和/或键。当然,您必须捕获所有鼠标和按键操作,因为单击任何小部件都会让您失去焦点。 - Maarten Bodewes
这并没有帮助,因为当按下TAB键时,KeyListener什么也不做...如果KeyListener能与TAB一起工作,我根本不会问这个问题... :) - DejanLekic
我明白你的意思,但如果我无法检测到使用TAB键导致的焦点丢失,那么追踪鼠标事件就毫无意义。而且,如果我能够获取TAB键,我根本不需要鼠标事件(因为其他事件都是鼠标事件,而它们已经被覆盖了)。请参见编辑2 - DejanLekic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接