设置JComboBox PopupMenu的大小

时间:2011-09-29 22:17:59

标签: java swing jcombobox popupmenu

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

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


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

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

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

进行测试:the component和一点点test programm如果要产生我的问题,您可以例如:

  • 键入" 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<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);     
    }



}

5 个答案:

答案 0 :(得分:7)

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

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

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

  3. 始终在event dispatch thread

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

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

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

    Combo image

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

答案 1 :(得分:5)

以下解决方案的基础是每次调用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个条目。我猜这是你的匹配逻辑的问题。

答案 2 :(得分:3)

未测试您的代码,

请提供有关呈现器here和AutoComplete JComboBox here的内容的建议

答案 3 :(得分:1)

使用扩展AbstractListModel&amp;的ComboboxModel中的Vector数组面临同样的问题。 实现MutableComboBoxModel。使用setMaximumRowCount:

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

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

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

答案 4 :(得分:-2)