Avatar billede jkrag Nybegynder
28. august 2002 - 16:57 Der er 2 kommentarer og
1 løsning

Swing-xpert: Flerniveau JPopupMenu i JComboBox

Jeg har en JComboBox hvorfra jeg gerne vil vælge en "Kategori".
"kategorierne" ligger i en træstruktur, og i ekspanderet tilstand er denne for lang at scrolle igennem. Derfor vil jeg gerne lave det med en JPopupMenu.
Popupmenuen er sådan set allerede lavet ud fra DB data, men jeg har lidt problemer med at få den vist fra comboboxen med en fornuftig funktionalitet.
Med dette mener jeg som minimum:
1) Når popup'en åbner skal den kunne vise den aktuelle kategori som selected (og dermed også expandere den nødvendige undermenu.
2) Man skal i et eller andet omfang kunne keyboardnavigere (som i en normal menu).
3) Når brugeren har valgt et element, skal comboboxen vise en streng genereret ud fra stien til elementet, f.eks. "Programmering > Java" eller "Programmering > ... > Swing" hvis der ikke er plads. (men denne del skal jeg nu nok få fikset selv).

Jeg har rodet en del med problemet og prøvet med combobox.setUI(minNedarvedeComboPopup);

Jeg har også prøvet bare at åbne min egen JPopupMenu ved MouseClicked på Combo'en, men ingen steder får jeg et fornuftigt resultat.

Jeg er nået til det punkt hvor jeg er overbevist om at jeg har overset noget indlysende. Default implementeringen af JCombBox bruger JPopupMenu, så det burde da være let at skifte den ud.

Er der nogen der har en ordentlig løsning på dette problem? (helst en jeg ikke allerede har prøvet :-)
Avatar billede logical Nybegynder
30. august 2002 - 10:30 #1
Hej Jan,

Omend det kaldes en popup i combobox regi, er den en smule forskellig i forhold til en popup menu. De to kan godt lægges sammen omend det bliver frygteligt besværligt.

Jeg havde selv problemet engang, og lavede en klasse til formålet. Den løser 1 og 2, og jeg har extracted noget omkring 3 i private String render(MenuElement[]), som kan give dig et hint om resten.Det ved jeg, at du kan fikse selv :-)

Den sender change events når der bliver highlighted et menupunkt, og når man taster enter i tekstfeltet, og ved at sammenligne indholdet med den givne selection vil man formodentlig kunne afgøre, om det er noget brugeren selv har skrevet.

Jeg har klippet javadoc væk, ellers er resten her:

import javax.swing.*;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.EventListenerList;
import javax.swing.plaf.basic.BasicArrowButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.*;

/**
* @author Niels Bech Nielsen, Logical
*/
public class TreeCombo extends JPanel{
    private JTextField tf;
    private JPopupMenu menu;
    private MenuElement[] selection= null;
    private int columns;

    private MenuSelectionManager manager;
    private ChangeEvent evt;
    private EventListenerList eventListeners = new EventListenerList();

    public TreeCombo(JPopupMenu menu, int columns) {
        evt = new ChangeEvent(this);
        manager = MenuSelectionManager.defaultManager();
        this.menu = menu;
        this.columns = columns;
        setup();
    }

    public boolean isEditable() {
        return tf.isEditable();
    }

    public void setEditable(boolean b) {
        tf.setEditable(b);
    }

    public String getSelectedText() {
        return tf.getText();
    }

    public MenuElement[] getSelection() {
            return selection;
    }


    public void addChangeListener(ChangeListener cl) {
        eventListeners.add(ChangeListener.class, cl);
    }

    public void removeChangeListener(ChangeListener cl) {
        eventListeners.remove(ChangeListener.class, cl);
    }

    private void setup() {
        tf = new JTextField(columns);
        tf.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                fireChangeEvent();
            }
        });
        super.add(tf);

        final BasicArrowButton arrow = new BasicArrowButton(BasicArrowButton.SOUTH);
        arrow.setRequestFocusEnabled(false);
        arrow.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                //xpand just below
                MenuElement[] preselect = selection;
                Dimension loc = arrow.getSize();
                menu.show(arrow, loc.height / 2, loc.width+5);
                if (preselect != null)
                    manager.setSelectedPath(preselect);
            }
        });
        super.add(arrow);

        // Read selections.
        manager.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                MenuElement[] items = manager.getSelectedPath();
                if (items.length == 0) return;
                // Must origin from our menu.
                if (items[0]  != menu) return;
                setSelection(items);
            }
        });
    }

    private void setSelection(MenuElement[] items) {
        selection = items;
        tf.setText(render(selection));
        fireChangeEvent();
    }

    private String render(MenuElement[] elements) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0 ; i < elements.length ; i++) {
            if (elements[i] instanceof JMenuItem) {
                JMenuItem item = (JMenuItem) elements[i];
                sb.append(item.getText()).append(" > ");
            }
        }
        if (sb.length() > 3)
            sb.delete(sb.length()-3, sb.length());

        return sb.toString();
    }

    private void fireChangeEvent() {
        ChangeListener[] list = (ChangeListener[])
                eventListeners.getListeners(ChangeListener.class);
        for (int i = 0; i < list.length; i++) {
            ChangeListener listener = list[i];
            listener.stateChanged(evt);
        }
    }
}


Her er et lille eksempel:

    public static void main(String[] args) {
        JFrame f = new JFrame("Combo test");

        JPanel p = new JPanel(new FlowLayout(FlowLayout.CENTER));

        final TreeCombo combo = new TreeCombo(createPopup(), 15);
        combo.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                MenuElement[] selection = combo.getSelection();
                System.out.println(combo.getSelectedText());
                System.out.println(selection.length);
            }
        });
        p.add(combo);

        f.setContentPane(p);

        f.setSize(400,400);
        f.setVisible(true);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private static JPopupMenu createPopup() {
        JPopupMenu menu = new JPopupMenu("Ok");
        menu.add("Framfab");
        menu.add("!rulez");
        JMenu m = new JMenu("By");
        m.add("Niels");
        m.add("Bech");
        m.add("Nielsen");
        menu.add(m);
        return menu;
    }
Avatar billede jkrag Nybegynder
30. august 2002 - 15:19 #2
Hej Niels.
Tak for et godt og grundigt svar på det allerførste eksperten spørgsmål jeg selv har stillet.
Jeg har virkelig været forundret over at der ikke var kommet en eneste kommentar på mit spørgsmål, men det har du i den grad lavet om på :-)
Den løsning havde jeg slet ikke overvejet. Og det virker jo smukt.
Du har sandlig fortjent dine point. :-)
Hilsen Jan
Avatar billede logical Nybegynder
30. august 2002 - 21:04 #3
Der er ikke så mange der kender swing i detaljer.. :-)

Det var også kun fordi titlen startede med swing-xprt, at jeg blev nysgerrig. Jeg har ellers ikke svaret et eneste spørgsmål i 2002.

Og mine 2 hade-componenter i swing er netop jcombobox og jpopupmenu, så dem har jeg patched et par gange.

Hils omkring dig.!
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester