3

我有一个JFrame看起来像这样的:

在此处输入图像描述

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

的特点之一JComboBox是它可以被赋予一个自定义编辑器。这些实现了ComboBoxEditor接口。在以下三种情况中的每一种情况下,GUI 看起来都完全相同,我希望它们的行为都完全相同:

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

当可编辑组合框的编辑器设置为默认值时,按下Tab会将焦点从顶部JTextField移到 上的编辑区域JComboBox,然后移到另一个JTextField。如果我创建了一个自定义编辑器,其编辑器组件是 aJTextField并且按照您的预期进行,同样的事情也会发生。

但是,如果我改为创建一个自定义编辑器,其编辑器组件是一个JPanel带有JTextField added 的编辑器,那么焦点会额外停止。如果焦点在顶部JTextField,则在移动到文本区域之前,按下Tab会将焦点移动到可编辑组合框右侧的小箭头。

为什么会这样?焦点永远不会移动到JPanel框架的底部,那么为什么会影响组合框上的 Tab 键顺序呢 JPanelJTextField

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

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;
    }
  }
} 

注意:如果我想将 a 添加JLabel到编辑器,此行为会成为问题。然后我必须在JPanel那里放置标签和文本字段。

4

3 回答 3

4

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

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

影响

  • 默认情况下,面板的可聚焦为真,因为组合的为真
  • 在其构造函数中强制面板的可聚焦为 false 无效(用户界面稍后会重置它以及每当切换 LAF 时)
  • 禁用组合的可聚焦也会禁用面板

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

private class PanelEditor extends JPanel implements ComboBoxEditor

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

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

另请注意,您可能会在使用复合编辑器时遇到更多问题(检查 BasicComboUI 的代码以了解更多假定纯无子组件的地方),因此您可能会考虑根本不这样做,而是想一种不同的方式来满足您的要求.

于 2013-08-30T06:55:25.017 回答
0

焦点没有转移到JPanel; 它正在转移到JComboBox自身。

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

setFocusable(false)

对于上面示例中的构造函数PanelEditor,那么奇怪的行为仍然存在,因为PanelEditor implements JPanel, 所以覆盖了. 的setFocusable方法。由于a 的方法基本上什么都不做,所以没有任何变化。JPanelJComboBoxsetFocusableJPanel

相反,如果您添加该行

panelEditorComboBox.setFocusable(false)

JFrame本身的构造函数JComboBox则将无法接收焦点,但JTextField编辑器内部会。这不是一个完美的解决方案,因为如果编辑器本身负责关闭 的可聚焦性会更好JComboBox,因此您始终可以将父JComboBox级作为参数传递给编辑器的构造函数,并打开可聚焦性离开那里。

我不知道为什么当您拥有 aJTextField作为编辑时行为会有所不同。一些奇怪的 Swing 东西。

于 2013-08-29T09:42:09.380 回答
0

尝试这个:

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

  addFocusListener(new FocusAdapter()
  {
    @Override
    public void focusGained(FocusEvent e)
    {
      inputTextField.requestFocusInWindow();
    }
  });
}
于 2013-08-28T16:05:39.963 回答