【问题标题】:Set Size of JComboBox PopupMenu设置 JComboBox PopupMenu 的大小
【发布时间】:2011-11-28 01:47:13
【问题描述】:

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

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


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

但结果将是一个闪烁的 PopupMenu :-(

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

用于测试:the component 和一点点test programm
要生成我的问题,您可以例如:

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

提前致谢

编辑: 我的目标是一个组合框,例如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<Object> itemBackup = new ArrayList<Object>();

    /**  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<String> notMatching = new ArrayList<String>();

                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 += "<b style=\"color: black;\">" + s.substring(start, start + sfl) + "</b>" + t;
                        start += sfl + t.length();
                    }
                }

                System.out.println(index + html);

                this.setText("<html><head></head><body style=\"color: gray;\">" + html + "</body></head>");
                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<Object> found = new ArrayList<Object>();

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



}

【问题讨论】:

  • 在论坛中发布您的SSCCE 代码,而不是使用指向其他网站的链接。
  • 请学习java命名约定并遵守它们

标签: java swing jcombobox popupmenu


【解决方案1】:

在扩展 AbstractListModel & 的 ComboboxModel 中使用 Vector 数组时遇到了同样的问题 实现 MutableComboBoxModel。使用 setMaximumRowCount 解决:

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

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

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

【讨论】:

    【解决方案2】:

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

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

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

    3. 始终在 event dispatch thread 上构建 GUI。

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

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

    更新:添加了验证@camickr 的solution 的代码。

    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<Object> itemBackup = new ArrayList<Object>();
    
            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<Object> found = new ArrayList<Object>();
                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<String> notMatching = new ArrayList<String>();
    
                    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 += "<b style=\"color: black;\">"
                                + s.substring(start, start + sfl) + "</b>" + t;
                            start += sfl + t.length();
                        }
                    }
                    this.setText("<html><head></head><body style=\"color: gray;\">"
                        + html + "</body></head>");
                    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};
                }
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      以下解决方案的基础是每次调用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<Object> itemBackup = new ArrayList<Object>();
      
          /**  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<String> notMatching = new ArrayList<String>();
      
                      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 += "<b style=\"color: black;\">" + s.substring(start, start + sfl) + "</b>" + t;
                              start += sfl + t.length();
                          }
                      }
      
                      this.setText("<html><head></head><body style=\"color: gray;\">" + html + "</body></head>");
                      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<Object> found = new ArrayList<Object>();
      
              //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();
              }
          }
      }
      

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

      【讨论】:

      • +1 我在com.apple.laf.AquaLookAndFeel 上看到了类似的结果。
      • 请学习 java 命名约定并遵守它们(无法抗拒:-)
      • 就是这样!!非常感谢 :-) 我的计划也是访问 PopupMenu,但我不知道该怎么做@kleopatra 你有命名约定的链接吗?我通常用 c++ 或 delphi/pascal 编程,而且我对 java 比较陌生,这可能会导致一些问题 ;-)
      【解决方案4】:

      没有测试你的代码,

      请对 Renderer here 和 AutoComplete JComboBox here 的内容提出建议

      【讨论】:

      • 好吧,我都检查了它们,但它们没有帮助“渲染器”可能是一个即将出现的问题,如果我用更多条目测试我的盒子;-) 另一个不使用PopupMenu 永久...我尝试在 Firefox 或 chrome 中获得像地址栏这样的组合框(没有网络搜索选项 ;-))
      • +1 这是另一个JComboBox variation
      • 这种变化非常好,但不是我真正需要的,因为这个版本似乎不支持可编辑的组合框,这是一种避免我的弹出刷新问题的方法,但不是我想要的想。此外,如果您键入“ball”,则不支持列出“football”。但无论如何谢谢......我现在要睡觉了,请不要指望我在下一个我们的答复内;-)
      【解决方案5】:

      【讨论】:

      • 是的,但结果是,ComboBox 失去了焦点,PopupMenu 崩溃了 -.-
      • -1 任何使用 updateUI() 方法的建议几乎肯定是错误的。 LAF 没有改变。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-04-15
      • 2013-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-20
      • 2012-07-01
      相关资源
      最近更新 更多