JCombobox 禁用项目选择(使combo只读)。

问题描述 投票:5回答:5

我想创建一个只读组合框。用户不能从弹出式列表中选择其他项目。这意味着弹出式列表不应该打开或者应该是空的。

我看到以下解决方案。

  • 设置一个只有一个项目(当前选中的项目)的组合框模型,这样当用户点击箭头按钮时,就会出现一个空列表。

  • 添加一个 PopupMenuListener 并在 popupMenuWillBecomeVisible 隐藏菜单。这是有问题的。我们必须调用 combo.hidePopup(); 从内 SwingUtilities.invokeLater()

空模型的方法似乎有点笨拙。第二种方法显示弹出列表的时间只有一小部分,短到足以让人注意到。这是非常丑陋的。

有没有第三种解决方案?

EDIT: 已实施的解决方案:

我实现了spungebob的建议方法,这是我的代码,供将来参考。

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

唯一的问题是Alt-Down键事件会使弹出菜单失效 即使我删除了所有的键监听器并清除了操作图。我通过使组合不可聚焦来规避这个问题。虽然不理想,但已经足够好了(-。

java swing selection jcombobox
5个回答
7
投票

这其实是一个很好的问题,涉及到Swing的一个局限性(并且困扰了我很久)。

我们需要一个只读的组合框,当...... (等等)......表单目前处于只读模式。 需要注意的是,用户在其他地方的输入可能会在瞬间将表单切换到编辑模式,所以切换JComponents(例如使用JLabel)在视觉上并不可取,我认为。 另外,请注意,一个被禁用的组合与一个只读的组合所传递给用户的信息是不一样的。

setEnabled(false) ->完全灰化;该组件 不能 与之互动;无论显示什么数据都是 相关和 不能 被选中进行复制粘贴。

setReadOnly(true) -粘贴的文本组件是 呈灰色(但箭头是);组件 不能 的互动;无论显示出什么数据 相关和 可以 被选中。

这样做的理由是,Swing 做了 的形式为JTextComponents实现。setEditable(boolean). 谢谢你们,但我也需要为JComboBox,JCheckbox,JRadioButton等。 我们不得不为这个缺失的API推出自己的版本。

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

Arrrgh!!!

所以,对这个问题。 你得把袖子卷起来一点。 对于一个 可编辑 combo。

  • 获取combo的编辑组件 combo.getEditor().getEditorComponent(). 这是一个JTextField。把它投出去,然后调用 setEditable(false). 这为你提供了你想要的combo的文本部分的功能和外观。

  • 通过迭代组合的getComponents()来获取组合的箭头组件。 这是你会发现的唯一一个AbstractButton。 调用 setEnabled(false). 这只是为了外观。

  • 找到所有的默认鼠标监听器,与组合(这应该是所有的人,如果你没有添加任何自己),并删除他们从组合和箭头按钮。

  • 保留这些监听器和箭头按钮的引用,以防你想把它切换回只读=假的时候。

或者类似的东西。 你的里程数可能会有所不同。

提示kleopatra为SwingX背书,它可能已经内置了这一功能(我不确定,我只是猜测)。

祝您好运。


1
投票

覆盖。

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

应该就可以了


0
投票

有什么不对,简单的 停用 的JComboBox?

setEnabled(false);

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 方法,以便在试图使用任何其他类型的模型时抛出一个异常。

编辑: 另外请注意,动作监听器仍然会被调用(选择不变)。


0
投票

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);
      }
   }
}
© www.soinside.com 2019 - 2024. All rights reserved.