带有可编辑JComboBoxes的奇怪选项卡顺序

时间:2013-08-28 14:38:07

标签: java swing focus jcombobox

我有JFrame看起来像这样:

enter image description here

它上面有两个JTextField,它们之间有一个JComboBox,底部有一个JPanel(你看不到)。

JComboBox的一个功能是可以为其提供自定义编辑器。这些实现了ComboBoxEditor接口。在以下三种情况中,GUI看起来完全相同,我希望它们的行为完全相同:

  • 我没有指定自定义编辑器,而是使用默认编辑器。
  • 我创建了一个自定义编辑器,其编辑器组件为JTextField
  • 我创建了一个自定义编辑器,其编辑器组件为JPanel,其上带有JTextField(使用BorderLayout)。

当可编辑组合框的编辑器设置为默认值时,按 Tab 会将焦点从顶部JTextField移动到JComboBox上的编辑区域,然后进入另一个JTextField。如果我创建一个自定义编辑器,其编辑器组件是JTextField,否则会执行您期望的操作,同样的事情就会发生。

但是,如果我改为创建一个自定义编辑器,其编辑器组件为JPanel并且JTextField add为其编辑,则焦点会增加一个停。如果焦点位于顶部JTextField,则按 Tab 会将焦点移动到可编辑组合框右侧的小箭头,然后再移动到文本区域。

为什么会这样?焦点永远不会移动到框架底部的JPanel,那么为什么持有JPanel的{​​{1}}会影响组合框中的Tab键顺序?

以下是S(-ish)SCCE,其上有一个文本字段和所有三种类型的组合框:

JTextField

注意:如果我想在编辑器中添加import javax.swing.*; import java.awt.event.*; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; public class ComboBoxTest extends JFrame { private JPanel layoutPanel; private JTextField meaninglessTextField; private JComboBox defaultEditorComboBox; private JComboBox textFieldEditorComboBox; private JComboBox panelEditorComboBox; public ComboBoxTest() { setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); layoutPanel = new JPanel(); layoutPanel.setLayout(new BoxLayout(layoutPanel, BoxLayout.Y_AXIS)); meaninglessTextField = new JTextField(); defaultEditorComboBox = new JComboBox(); // Just a default JComboBox. defaultEditorComboBox.setEditable(true); textFieldEditorComboBox = new JComboBox(); textFieldEditorComboBox.setEditable(true); textFieldEditorComboBox.setEditor(new TextFieldEditor()); panelEditorComboBox = new JComboBox(); panelEditorComboBox.setEditable(true); panelEditorComboBox.setEditor(new PanelEditor()); layoutPanel.add(Box.createRigidArea(new Dimension(500,0))); layoutPanel.add(meaninglessTextField); layoutPanel.add(defaultEditorComboBox); layoutPanel.add(textFieldEditorComboBox); layoutPanel.add(panelEditorComboBox); Container contentPane = getContentPane(); contentPane.add(layoutPanel, BorderLayout.CENTER); pack(); } public static void main(String[] args) { java.awt.EventQueue.invokeLater(new Runnable() { @Override public void run() { new ComboBoxTest().setVisible(true); } }); } private class PanelEditor extends JPanel implements ComboBoxEditor { public JTextField inputTextField = new JTextField(); public PanelEditor() { setLayout(new BorderLayout()); add(inputTextField, BorderLayout.CENTER); } @Override public String getItem() { return inputTextField.getText(); } @Override public void setItem(Object newText) { if (newText != null) { inputTextField.setText(newText.toString()); } else { inputTextField.setText(""); } } @Override public Component getEditorComponent() { return this; } @Override public void removeActionListener(ActionListener listener) { inputTextField.removeActionListener(listener); } @Override public void addActionListener(ActionListener listener) { inputTextField.addActionListener(listener); } @Override public void selectAll() { inputTextField.selectAll(); } } private class TextFieldEditor extends PanelEditor implements ComboBoxEditor { // The same, except that the editor component is now just the JTextField // rather than the whole panel. public TextFieldEditor() { } @Override public JTextField getEditorComponent() { return inputTextField; } } } ,则此行为会成为问题。然后我必须在JLabel放置标签和文本字段。

3 个答案:

答案 0 :(得分:4)

基本问题是组合的ui委托无法处理复合编辑器组件。有几个地方假设编辑器组件是它需要做的任何配置的目标。这里具体的错误行为是它明确地将编辑器的可聚焦性设置为组合本身的可聚焦性

// in BasicComboBoxUI
protected void configureEditor() {
    ....
    editor.setFocusable(comboBox.isFocusable());
    ....
]

影响

  • 默认情况下,面板的焦点是真的,因为组合是真的
  • 在其构造函数中强制将panel的focusable设置为false没有任何效果(ui会在以后和LAF切换时重置它)
  • 禁用组合的可聚焦功能也会禁用面板

要修复编辑器的级别,您可以实现其isFocusable无条件地返回false:

private class PanelEditor extends JPanel implements ComboBoxEditor

    public boolean isFocusable() {
        return false;
    }
    ...
}

抛开:为了代码卫生,最好不要扩展一个视图来实现其作为ComboBoxEditor的角色(即使在这里你需要一个子类化的JPanel来避免这个问题,所以它可以说是临界的:-) - 而是实现编辑器,让它使用调整的面板。

还要注意你可能会遇到更多的复合编辑器问题(检查BasicComboUI的代码以获取更多地方假设一个普通的无子组件),所以你可能会考虑不要这样做但是想到一个不同的方法达到你的要求。

答案 1 :(得分:0)

试试这个:

public PanelEditor()
{
  // other code...

  addFocusListener(new FocusAdapter()
  {
    @Override
    public void focusGained(FocusEvent e)
    {
      inputTextField.requestFocusInWindow();
    }
  });
}

答案 2 :(得分:0)

重点不是转移到JPanel;它正在转移到JComboBox本身。

您可以使用setFocusable方法阻止组件接收焦点。如果添加行

setFocusable(false)

到上面示例中的PanelEditor的构造函数,然后奇怪的行为仍然存在,因为PanelEditor implements JPanel,所以setFocusable方法{ {1}}会覆盖JPanel的内容。由于JComboBox的{​​{1}}方法基本上什么都不做,所以没有任何改变。

如果改为添加行

setFocusable

JPanel本身的构造函数,然后panelEditorComboBox.setFocusable(false) 将无法获得焦点,但编辑器中的JFrame将会。这不是一个完美的解决方案,因为如果编辑器本身负责关闭JComboBox的可聚焦性会更好,所以你总是可以将父JTextField作为参数传递给编辑器的构造函数,并关闭了焦点。

当你有一个JComboBox作为编辑器时,我不知道为什么行为会有所不同。一些奇怪的Swing的事情。