Установить размер JComboBox PopupMenu - PullRequest
4 голосов
/ 30 сентября 2011

Я программирую пользовательский компонент, который расширяет JComboBox. Моя проблема в том, что PopupMenu не актуализирует свой размер, если я добавляю или удаляю элемент. Так, например, есть 2 элемента в списке, но если раньше их было 4, в PopupMenu также было 2 "пустых" элемента.

Единственный найденный мной обходной путь - это сделать (в строке 213 JIntelligentComboBox.java)

<br> this.setPopupVisible(false); <br>this.setPopupVisible(true);

но результатом будет вспыхивающее PopupMenu: - (

Так, что еще я мог сделать, чтобы обновить / перекрасить PopupMenu без мерцания?

Для тестирования: компонент и небольшая тестовая программа
Для генерации моей проблемы вы можете, например,

  • тип "е"
  • нажмите "возврат"
  • тип "м"

Заранее спасибо

Edit: Моя цель - 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 ]

7 голосов
/ 30 сентября 2011

Я пересмотрел вашу sscce ниже и заметил несколько вещей:

  1. Наблюдаемая вами аномалия не очевидна при использовании apple.laf.AquaComboBoxUI.В частности, ввод и удаление текста увеличивают и уменьшают список, как и ожидалось.Вы можете попробовать пересмотренный код на своей платформе.

  2. Я переключился с KeyListener на KeyAdapter для удобства, но это не решение.Вы, вероятно, должны использовать DocumentListener.Он не может быть изменен во время использования, как вы делаете сейчас, поэтому я не стал заниматься этим дальше.

  3. Всегда создавайте графический интерфейс в потоке отправки событий .

  4. Жестко закодированные размеры и новые шрифты редко выглядят правильно в других реализациях внешнего вида.Я просто удалил ваш, чтобы получить показанный вид.

  5. Ваш конструктор изменяет модель после создания родительского элемента, поэтому результат зависит от порядка создания экземпляров.Отдельной моделью может быть проще управлять.

Обновление: добавлен код для проверки решения @ camickr .

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};
            }
        }
    }
}
5 голосов
/ 30 сентября 2011

Основой решения, приведенного ниже, является изменение размера всплывающего окна при каждом вызове 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 записи.Я предполагаю, что это проблема с вашей логикой сопоставления.

3 голосов
/ 30 сентября 2011

не проверен ваш код,

пожалуйста, советуйте для содержания Renderer здесь и для автозаполнения JComboBox здесь

1 голос
/ 11 января 2012

Столкнулся с той же проблемой, используя массив Vector в ComboboxModel, который расширяет AbstractListModel & реализует MutableComboBoxModel. Решено с помощью setMaximumRowCount:

  1. перебрать значения базы данных и сохранить их в public ArrayList listInCombobox = new ArrayList ();

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

  3. сделать выше в myComboBox MouseListener (mousePressed).

0 голосов
/ 30 сентября 2011
...