设置 JComboBox PopupMenu 的大小

发布于 2024-12-07 04:28:20 字数 9162 浏览 7 评论 0原文

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

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


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

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

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

用于测试: 组件 和一点测试程序
要生成我的问题,您可以例如:

  • 输入“e”
  • 按“return”
  • 输入“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);     
    }



}

i am programming a custom component which extends a JComboBox. My problem is, the PopupMenu won't actualise its size if i am adding or removing an item. So there are e.g. 2 items in the list, but if there were 4 before i had 2 "empty" items in the PopupMenu as well.

The only workaround i found was to do (in JIntelligentComboBox.java line 213)


this.setPopupVisible(false);

this.setPopupVisible(true);

but the result will be a flickering PopupMenu :-(

So what else could i do to refresh/repaint the PopupMenu without flickering?

For testing: the component and a little test programm
To generate my problem you could e.g.:

  • type "e"
  • press "return"
  • type "m"

Thanks in advance

Edit:
My goal is a ComboBox that acts like e.g. the adressbar in Firefox or Chrome, i want to show all items of the PopupMenu that contain the typed chars.

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



}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

夏天碎花小短裙 2024-12-14 04:28:20

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

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

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

  3. 始终在事件调度线程上构建 GUI .

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

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

更新:添加了代码来验证 @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<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};
            }
        }
    }
}

I have revised your sscce below, and I noticed a few things:

  1. The anomaly you observe is not apparent when using apple.laf.AquaComboBoxUI. In particular, entering and deleting text grows and shrinks the list as expected. You might try the revised code on your platform.

  2. I switched from KeyListener to KeyAdapter for expedience, but that's not a solution. You should probably use a DocumentListener. It can't be mutated while in use, as you are doing now, so I didn't pursue this further.

  3. Always build the GUI on the event dispatch thread.

  4. Hard-coded dimensions and novel fonts rarely look right on other look & feel implementations. I simply removed yours to get the appearance shown.

  5. Your constructor modifies the model after constructing the parent, so the result depends on the order of instantiation. A separate model might be easier to manage.

Update: Added code to verify @camickr's 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};
            }
        }
    }
}
与君绝 2024-12-14 04:28:20

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

The basis of the solution below is to resize the popup every time you invoke the searchAndListRoutine. You need to take into account that the popup can be displayed in its own Window when the popup displays outside the bounds of the parent frame or it can be displayed on the layered pane of the parent frame:

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

The one issue is that when the combo box field is empty the model contains 4 entries. I would guess that is a problem with your matching logic.

当梦初醒 2024-12-14 04:28:20

未测试您的代码,

请在此处提供渲染器内容的建议对于自动完成 JComboBox 此处

not tested your code,

please advice for content of Renderer here and for AutoComplete JComboBox here

飘落散花 2024-12-14 04:28:20

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

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

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

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

Faced the same problem using Vector array in ComboboxModel that extends AbstractListModel &
implements MutableComboBoxModel. Solved using setMaximumRowCount:

  1. iterate over database values and save them into
    public ArrayList listInCombobox = new ArrayList();

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

  3. do the above inside myComboBox MouseListener (mousePressed).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文