JCombobox禁用项目选择(make readboly)

时间:2014-05-06 16:32:29

标签: java swing selection jcombobox

我想创建一个只读组合框。用户不应该从弹出列表中选择另一个项目。这意味着弹出列表不应该打开或应该为空。

我看到以下解决方案:

  • 设置仅包含一个项目(当前所选项目)的ComboBox模型,因此当用户单击箭头按钮时,会显示一个空列表。

  • 添加PopupMenuListener并在popupMenuWillBecomeVisible隐藏菜单中。这有问题:我们必须在combo.hidePopup();

  • 内拨打SwingUtilities.invokeLater()

空模型方法看起来有点笨重。第二种方法显示弹出列表只有几分之一秒,足够短以至于被注意到。这非常难看。

有第三种解决方案吗?

编辑:已实施解决方案

我从splungebob实现了建议的方法,这是我的代码供将来参考:

private void makeComboReadonly() {
  Component editorComponent = box.getEditor().getEditorComponent();
  if (editorComponent instanceof JTextField) {
    ((JTextField) editorComponent).setEditable(false);
  }

  for (Component childComponent : box.getComponents()) {
    if (childComponent instanceof AbstractButton) {
      childComponent.setEnabled(false);
      final MouseListener[] listeners = childComponent.getListeners(MouseListener.class);
      for (MouseListener listener : listeners) {
        childComponent.removeMouseListener(listener);
      }
    }
  }

  final MouseListener[] mouseListeners = box.getListeners(MouseListener.class);
  for (MouseListener listener : mouseListeners) {
    box.removeMouseListener(listener);
  }

  final KeyListener[] keyListeners = box.getListeners(KeyListener.class);
  for (KeyListener keyListener : keyListeners) {
    box.removeKeyListener(keyListener);
  }

  box.setFocusable(false);

  //box.getActionMap().clear(); //no effect
  //box.getInputMap().clear();
}

唯一的问题是Key-Event Alt-Down,即使我删除所有关键监听器并清除动作映射,它也会弹出弹出菜单。我通过使组合不可聚焦来规避这个问题。不理想但足够好( - :

5 个答案:

答案 0 :(得分:7)

这实际上是一个关于Swing限制之一的好问题(并且长期以来一直困扰着我)。

当...(等待它)时,需要一个只读组合框...表单当前处于只读模式。请注意,来自用户elswhere的输入可能会在一瞬间将表单翻转为编辑模式,因此切换JComponents(例如使用JLabel)在视觉上是不可取的,IMO。另请注意,禁用的组合不会向用户传达与只读组合相同的信息:

setEnabled(false) - >完全变灰了;组件不能与之互动;无论显示的数据是相关,不能选择复制/粘贴。

setReadOnly(true) - >组合的文本组件变灰(但箭头是);组件不能与之互动;任何可能显示的数据 相关且可以被选中。

对此的理由是,Swing setEditable(boolean)的形式为JTextComponents实现此功能。谢谢大家,但我也需要JComboBox,JCheckbox,JRadioButton等。我们不得不为这个缺失的API推出我们自己的版本。

另一个Swing gaffe(IHMO)是不一致的API。 JTextComponent.setEditable(boolean)强制执行只读行为,而JComboBox.setEditable(boolean)则不执行。

Arrrgh !!!

所以,问题。你得卷起袖子。对于可编辑的组合:

  • 通过combo.getEditor().getEditorComponent()获取组合编辑器组件。它是一个JTextField。投下它,然后拨打setEditable(false)。这为您提供了组合的文本部分所需的功能和外观。

  • 通过迭代组合的getComponents()来获取组合的箭头组件。它是您唯一能找到的AbstractButton。致电setEnabled(false)。这仅用于外观。

  • 查找组合附带的所有默认鼠标侦听器(如果您没有自己添加任何内容,则应该是所有鼠标侦听器)并从组合和箭头按钮中删除它们。

  • 保留对这些侦听器和箭头按钮的引用,以防您在只读= false时将其切换回来。

或类似的东西。你的旅费可能会改变。

Cue kleopatra赞同SwingX,它可能已经内置了这个功能(我当然不知道,我只是在猜测)。

祝你好运。

答案 1 :(得分:1)

重写:

@Override
public void showPopup()
{
    //do nothing
}

应该这样做。

答案 2 :(得分:1)

splungebob提供了完美的解决方案。这是他的注释变成了代码,以便您可以抓住它并继续:

private void setJComboBoxReadOnly(JComboBox jcb)
{
   JTextField jtf = (JTextField)jcb.getEditor().getEditorComponent();
   jtf.setEditable(false);

   MouseListener[] mls = jcb.getMouseListeners();
   for (MouseListener listener : mls)
      jcb.removeMouseListener(listener);

   Component[] comps = jcb.getComponents();
   for (Component c : comps)
   {
      if (c instanceof AbstractButton)
      {
         AbstractButton ab = (AbstractButton)c;
         ab.setEnabled(false);

         MouseListener[] mls2 = ab.getMouseListeners();
         for (MouseListener listener : mls2)
            ab.removeMouseListener(listener);
      }
   }
}

答案 3 :(得分:0)

简单地禁用 JComboBox有什么问题?

setEnabled(false);

答案 4 :(得分:0)

我有类似的要求。调用setEnabled(false)会出现可怕的外观,用户无法浏览下拉列表。覆盖showPopup()不起作用。尝试通过invokeLater侦听菜单打开然后关闭它会导致菜单闪烁,用户再次无法浏览菜单。

最后我做了这个(不是说它完美,但它完全符合我的要求):

import javax.swing.JComboBox;

public class ReadOnlyComboBox<E> extends JComboBox<E>
{
    private static final long serialVersionUID = 5866761337995322114L;

    public ReadOnlyComboBox()
    {
        this.setModel(new ReadOnlyComboBoxModel<E>());
    }

    public void setReadOnly(boolean readOnly)
    {
       ((ReadOnlyComboBoxModel<E>)this.getModel()).setReadOnly(readOnly);
    }
}

import javax.swing.DefaultComboBoxModel;

public class ReadOnlyComboBoxModel<E> extends DefaultComboBoxModel<E>
{
    private static final long serialVersionUID = -1923833835224513983L;
    private boolean readOnly;

    @Override
    public void setSelectedItem(Object anItem)
    {
        if(!readOnly)
            super.setSelectedItem(anItem);
    }

    public void setReadOnly(boolean readOnly)
    {
        this.readOnly = readOnly;
    }
}

如果需要,您需要在setReadOnly(false)上调用ReadOnlyComboBox,然后以编程方式设置所选项目,然后将其重新设置为停止用户进行选择。

请注意未经检查的强制转换,在我的小程序中对我来说不是问题,但是如果尝试使用任何其他类型的模型,应该覆盖setModel方法来查找异常。< / p>

编辑:还要注意仍然会调用动作侦听器(选择不变)。