设置JComboBox PopupMenu的大小

我正在编写一个扩展JComboBox的自定义组件。 我的问题是,如果我要添加或删除项目,PopupMenu将不会实现其大小。 所以列表中有2个项目,但是如果我在PopupMenu中有2个“空”项目之前有4个项目。

我发现的唯一解决方法是做(在JIntelligentComboBox.java第213行)


this.setPopupVisible(false);
this.setPopupVisible(true);

但结果将是一个闪烁的PopupMenu 🙁

那么我还能做些什么来刷新/重绘PopupMenu而不会闪烁?

用于测试: 组件和一点测试程序
要产生我的问题,你可以:

  • 输入“e”
  • 按“返回”
  • 输入“m”

提前致谢

编辑:我的目标是一个ComboBox,其行为类似于Firefox或Chrome中的地址栏,我想显示包含类型字符的PopupMenu的所有项目。

cboxtester.java:

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JList; import javax.swing.plaf.basic.BasicComboBoxRenderer; public class cboxtester extends JFrame { private DefaultComboBoxModel dcm = new DefaultComboBoxModel(new Object[][] {new Object[] {"Mittagessen", "", 0}, new Object[] {"Essen", "", 0}, new Object[] {"Frühstück", "", 0}, new Object[] {"Abendessen", "", 0}}); private JIntelligentComboBox icb = new JIntelligentComboBox(dcm); private cboxtester(){ this.add(icb, BorderLayout.CENTER); this.add(new JButton("bla"), BorderLayout.EAST); this.pack(); this.setVisible(true); this.setDefaultCloseOperation(EXIT_ON_CLOSE); } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub cboxtester cbt = new cboxtester(); } } 

JIntelligentComboBox.java:

 import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.Vector; import javax.swing.ComboBoxEditor; import javax.swing.ComboBoxModel; import javax.swing.DefaultListCellRenderer; import javax.swing.DefaultRowSorter; import javax.swing.JComboBox; import javax.swing.JList; import javax.swing.JTextField; import javax.swing.ListCellRenderer; import javax.swing.MutableComboBoxModel; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import javax.swing.plaf.basic.BasicComboBoxEditor; import javax.swing.plaf.basic.BasicComboBoxRenderer; import javax.swing.plaf.metal.MetalComboBoxEditor; public class JIntelligentComboBox extends JComboBox { private ArrayList itemBackup = new ArrayList(); /** Initisiert die JIntelligentComboBox */ private void init(){ class searchComboBoxEditor extends BasicComboBoxEditor { public searchComboBoxEditor(){ super(); } @Override public void setItem(Object anObject){ if (anObject == null) { super.setItem(anObject); } else { Object[] o = (Object[]) anObject; super.setItem(o[0]); } } @Override public Object getItem(){ return new Object[]{super.getItem(), super.getItem(), 0}; } } this.setEditor(new searchComboBoxEditor()); this.setEditable(true); class searchRenderer extends BasicComboBoxRenderer { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus){ if (index == 0) { setText(""); this.setPreferredSize(new Dimension(1, 1)); return this; } this.setPreferredSize(new Dimension(160, 17)); if (index == list.getModel().getSize() - 1) { this.setBorder(new EmptyBorder(0, 3, 1, 3)); } else { this.setBorder(new EmptyBorder(0, 3, 0, 3)); } Object[] v = (Object[]) value; //System.out.println(v[0]); this.setFont(new Font("Arial", Font.PLAIN, 12)); this.setBackground(Color.white); String s = (String) v[0]; String lowerS = s.toLowerCase(); String sf = (String) v[1]; String lowerSf = sf.toLowerCase(); ArrayList notMatching = new ArrayList(); if (!sf.equals("")){ int fs = -1; int lastFs = 0; while ((fs = lowerS.indexOf((String) lowerSf, (lastFs == 0) ? -1 : lastFs)) > -1) { notMatching.add(s.substring(lastFs, fs)); lastFs = fs + sf.length(); //System.out.println(fs+sf.length()); } notMatching.add(s.substring(lastFs)); //System.out.println(notMatching); } String html = ""; if (notMatching.size() > 1) { html = notMatching.get(0); int start = html.length(); int sfl = sf.length(); for (int i = 1; i < notMatching.size(); i++) { String t = notMatching.get(i); html += "" + s.substring(start, start + sfl) + "" + t; start += sfl + t.length(); } } System.out.println(index + html); this.setText("" + html + ""); return this; } } this.setRenderer(new searchRenderer()); // leeres Element oben einfügen int size = this.getModel().getSize(); Object[] tmp = new Object[this.getModel().getSize()]; for (int i = 0; i < size; i++) { tmp[i] = this.getModel().getElementAt(i); itemBackup.add(tmp[i]); } this.removeAllItems(); this.getModel().addElement(new Object[]{"", "", 0}); for (int i = 0; i < tmp.length; i++) { this.getModel().addElement(tmp[i]); } // keylistener hinzufügen this.getEditor().getEditorComponent().addKeyListener(new KeyListener() { @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub searchAndListEntries(((JTextField)JIntelligentComboBox.this.getEditor().getEditorComponent()).getText()); //System.out.println(((JTextField)JIntelligentComboBox.this.getEditor().getEditorComponent()).getText()); } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } }); } public JIntelligentComboBox(){ super(); } public JIntelligentComboBox(MutableComboBoxModel aModel){ super(aModel); init(); } public JIntelligentComboBox(Object[] items){ super(items); init(); } public JIntelligentComboBox(Vector items){ super(items); init(); } @Override public MutableComboBoxModel getModel(){ return (MutableComboBoxModel) super.getModel(); } private void searchAndListEntries(Object searchFor){ ArrayList found = new ArrayList(); //System.out.println("sf: "+searchFor); for (int i = 0; i < this.itemBackup.size(); i++) { Object tmp = this.itemBackup.get(i); if (tmp == null || searchFor == null) continue; Object[] o = (Object[]) tmp; String s = (String) o[0]; if (s.matches("(?i).*" + searchFor + ".*")){ found.add(new Object[]{((Object[])tmp)[0], searchFor, ((Object[])tmp)[2]}); } } this.removeAllItems(); this.getModel().addElement(new Object[] {searchFor, searchFor, 0}); for (int i = 0; i < found.size(); i++) { this.getModel().addElement(found.get(i)); } this.setPopupVisible(true); } } 

下面解决方案的基础是每次调用searchAndListRoutine时调整弹出窗口的searchAndListRoutine 。 您需要考虑当弹出窗口显示在父框架的边界之外时弹出窗口可以显示在其自己的窗口中,或者它可以显示在父框架的分层窗格上:

 import java.awt.*; import java.awt.event.*; import java.util.ArrayList; import java.util.Vector; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.plaf.basic.*; import javax.swing.plaf.metal.*; import javax.swing.plaf.basic.*; public class JIntelligentComboBox extends JComboBox { private ArrayList itemBackup = new ArrayList(); /** Initisiert die JIntelligentComboBox */ private void init(){ class searchComboBoxEditor extends BasicComboBoxEditor { public searchComboBoxEditor(){ super(); } @Override public void setItem(Object anObject){ if (anObject == null) { super.setItem(anObject); } else { Object[] o = (Object[]) anObject; super.setItem(o[0]); } } @Override public Object getItem(){ return new Object[]{super.getItem(), super.getItem(), 0}; } } this.setEditor(new searchComboBoxEditor()); this.setEditable(true); class searchRenderer extends BasicComboBoxRenderer { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus){ if (index == 0) { setText(""); this.setPreferredSize(new Dimension(1, 1)); return this; } this.setPreferredSize(new Dimension(160, 17)); if (index == list.getModel().getSize() - 1) { this.setBorder(new EmptyBorder(0, 3, 1, 3)); } else { this.setBorder(new EmptyBorder(0, 3, 0, 3)); } Object[] v = (Object[]) value; //System.out.println(v[0]); this.setFont(new Font("Arial", Font.PLAIN, 12)); this.setBackground(Color.white); String s = (String) v[0]; String lowerS = s.toLowerCase(); String sf = (String) v[1]; String lowerSf = sf.toLowerCase(); ArrayList notMatching = new ArrayList(); if (!sf.equals("")){ int fs = -1; int lastFs = 0; while ((fs = lowerS.indexOf((String) lowerSf, (lastFs == 0) ? -1 : lastFs)) > -1) { notMatching.add(s.substring(lastFs, fs)); lastFs = fs + sf.length(); //System.out.println(fs+sf.length()); } notMatching.add(s.substring(lastFs)); //System.out.println(notMatching); } String html = ""; if (notMatching.size() > 1) { html = notMatching.get(0); int start = html.length(); int sfl = sf.length(); for (int i = 1; i < notMatching.size(); i++) { String t = notMatching.get(i); html += "" + s.substring(start, start + sfl) + "" + t; start += sfl + t.length(); } } this.setText("" + html + ""); return this; } } this.setRenderer(new searchRenderer()); // int size = this.getModel().getSize(); Object[] tmp = new Object[this.getModel().getSize()]; for (int i = 0; i < size; i++) { tmp[i] = this.getModel().getElementAt(i); itemBackup.add(tmp[i]); } this.removeAllItems(); this.getModel().addElement(new Object[]{"", "", 0}); for (int i = 0; i < tmp.length; i++) { this.getModel().addElement(tmp[i]); } // this.getEditor().getEditorComponent().addKeyListener(new KeyListener() { @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub searchAndListEntries(((JTextField)JIntelligentComboBox.this.getEditor().getEditorComponent()).getText()); //System.out.println(((JTextField)JIntelligentComboBox.this.getEditor().getEditorComponent()).getText()); } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } }); } public JIntelligentComboBox(){ super(); } public JIntelligentComboBox(MutableComboBoxModel aModel){ super(aModel); init(); } public JIntelligentComboBox(Object[] items){ super(items); init(); } public JIntelligentComboBox(Vector items){ super(items); init(); } @Override public MutableComboBoxModel getModel(){ return (MutableComboBoxModel) super.getModel(); } private void searchAndListEntries(Object searchFor){ ArrayList found = new ArrayList(); //System.out.println("sf: "+searchFor); for (int i = 0; i < this.itemBackup.size(); i++) { Object tmp = this.itemBackup.get(i); if (tmp == null || searchFor == null) continue; Object[] o = (Object[]) tmp; String s = (String) o[0]; if (s.matches("(?i).*" + searchFor + ".*")){ found.add(new Object[]{((Object[])tmp)[0], searchFor, ((Object[])tmp)[2]}); } } this.removeAllItems(); this.getModel().addElement(new Object[] {searchFor, searchFor, 0}); for (int i = 0; i < found.size(); i++) { this.getModel().addElement(found.get(i)); } //this.setPopupVisible(true); int size = this.getModel().getSize() - 1; System.out.println("Elements: " + size); if (size == 0) { this.setPopupVisible( false ); return; } this.setPopupVisible(true); BasicComboPopup popup = (BasicComboPopup)this.getAccessibleContext().getAccessibleChild(0); Window popupWindow = SwingUtilities.windowForComponent(popup); Window comboWindow = SwingUtilities.windowForComponent(this); if (comboWindow.equals(popupWindow)) { Component c = popup.getParent(); Dimension d = c.getPreferredSize(); c.setSize(d); } else { popupWindow.pack(); } } } 

一个问题是,当combobox字段为空时,模型包含4个条目。 我猜这是你的匹配逻辑的问题。

我在下面修改了你的sscce ,我注意到了一些事情:

  1. 使用apple.laf.AquaComboBoxUI时,您观察到的exception现象并不明显。 特别是,输入和删除文本会按预期增长和缩小列表。 您可以尝试在您的平台上修改代码。

  2. 我为了方便而从KeyListener切换到KeyAdapter ,但这不是解决方案。 您应该使用DocumentListener 。 正如你现在所做的那样,它在使用时不能变异,所以我没有进一步追求。

  3. 始终在事件派发线程上构建GUI。

  4. 硬编码尺寸和新颖字体在其他外观和感觉实现上很少看起来正确。 我只是删除了你的外观以显示外观。

  5. 构造函数在构造父类后修改模型,因此结果取决于实例化的顺序。 单独的模型可能更容易管理。

更新:添加了validation@ camickr 解决方案的代码。

组合图像

 import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Window; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.List; import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JList; import javax.swing.JTextField; import javax.swing.MutableComboBoxModel; import javax.swing.SwingUtilities; import javax.swing.plaf.basic.BasicComboBoxEditor; import javax.swing.plaf.basic.BasicComboBoxRenderer; import javax.swing.plaf.basic.BasicComboPopup; public class CBoxTest extends JFrame { private CBoxTest() { DefaultComboBoxModel dcm = new DefaultComboBoxModel(); StringBuilder s = new StringBuilder(); for (char i = 'a'; i < 'm'; i++) { s.append(i); dcm.addElement(new Object[]{s.toString(), "", 0}); } JIntelligentComboBox icb = new JIntelligentComboBox(dcm); this.setDefaultCloseOperation(EXIT_ON_CLOSE); this.add(icb, BorderLayout.CENTER); this.add(new JButton("Button"), BorderLayout.EAST); this.pack(); this.setLocationRelativeTo(null); this.setVisible(true); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { CBoxTest cbt = new CBoxTest(); } }); } class JIntelligentComboBox extends JComboBox { private List itemBackup = new ArrayList(); public JIntelligentComboBox(MutableComboBoxModel aModel) { super(aModel); init(); } private void init() { this.setRenderer(new searchRenderer()); this.setEditor(new searchComboBoxEditor()); this.setEditable(true); int size = this.getModel().getSize(); Object[] tmp = new Object[this.getModel().getSize()]; for (int i = 0; i < size; i++) { tmp[i] = this.getModel().getElementAt(i); itemBackup.add(tmp[i]); } this.removeAllItems(); this.getModel().addElement(new Object[]{"", "", 0}); for (int i = 0; i < tmp.length; i++) { this.getModel().addElement(tmp[i]); } final JTextField jtf = (JTextField) this.getEditor().getEditorComponent(); jtf.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { searchAndListEntries(jtf.getText()); } }); } @Override public MutableComboBoxModel getModel() { return (MutableComboBoxModel) super.getModel(); } private void searchAndListEntries(Object searchFor) { List found = new ArrayList(); for (int i = 0; i < this.itemBackup.size(); i++) { Object tmp = this.itemBackup.get(i); if (tmp == null || searchFor == null) { continue; } Object[] o = (Object[]) tmp; String s = (String) o[0]; if (s.matches("(?i).*" + searchFor + ".*")) { found.add(new Object[]{ ((Object[]) tmp)[0], searchFor, ((Object[]) tmp)[2] }); } } this.removeAllItems(); this.getModel().addElement(new Object[]{searchFor, searchFor, 0}); for (int i = 0; i < found.size(); i++) { this.getModel().addElement(found.get(i)); } this.setPopupVisible(true); // https://stackoverflow.com/questions/7605995 BasicComboPopup popup = (BasicComboPopup) this.getAccessibleContext().getAccessibleChild(0); Window popupWindow = SwingUtilities.windowForComponent(popup); Window comboWindow = SwingUtilities.windowForComponent(this); if (comboWindow.equals(popupWindow)) { Component c = popup.getParent(); Dimension d = c.getPreferredSize(); c.setSize(d); } else { popupWindow.pack(); } } class searchRenderer extends BasicComboBoxRenderer { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { if (index == 0) { setText(""); return this; } Object[] v = (Object[]) value; String s = (String) v[0]; String lowerS = s.toLowerCase(); String sf = (String) v[1]; String lowerSf = sf.toLowerCase(); List notMatching = new ArrayList(); if (!sf.equals("")) { int fs = -1; int lastFs = 0; while ((fs = lowerS.indexOf(lowerSf, (lastFs == 0) ? -1 : lastFs)) > -1) { notMatching.add(s.substring(lastFs, fs)); lastFs = fs + sf.length(); } notMatching.add(s.substring(lastFs)); } String html = ""; if (notMatching.size() > 1) { html = notMatching.get(0); int start = html.length(); int sfl = sf.length(); for (int i = 1; i < notMatching.size(); i++) { String t = notMatching.get(i); html += "" + s.substring(start, start + sfl) + "" + t; start += sfl + t.length(); } } this.setText("" + html + ""); return this; } } class searchComboBoxEditor extends BasicComboBoxEditor { public searchComboBoxEditor() { super(); } @Override public void setItem(Object anObject) { if (anObject == null) { super.setItem(anObject); } else { Object[] o = (Object[]) anObject; super.setItem(o[0]); } } @Override public Object getItem() { return new Object[]{super.getItem(), super.getItem(), 0}; } } } } 

没有测试你的代码,

请在此处获取Renderer内容和AutoComplete JComboBox的建议

使用ComboboxModel中的Vector数组面临同样的问题,它扩展了AbstractListModel并实现了MutableComboBoxModel。 使用setMaximumRowCount解决:

  1. 迭代数据库值并将它们保存到public ArrayList listInCombobox = new ArrayList();

  2. myComboBox.setMaximumRowCount(listInCombobox.size());

  3. 在myComboBox MouseListener(mousePressed)中执行上述操作。