具有不同弹出组件的JComboBox的奇怪行为

时间:2017-05-31 12:27:47

标签: java swing jcombobox

尝试使用一些"黑暗的一面"当我尝试使用某些组件填充组合框的弹出框时,Swing的功能和问题。这是我的SSCCE:

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DateFormatSymbols;

import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.plaf.basic.ComboPopup;

public class ComboBoxTryout extends JComboBox {

    private JPanel panel;

    public ComboBoxTryout() {
        initPanel();
    }

    private void initPanel() {
        panel = new JPanel(new GridLayout(7, 1, 5, 5));
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        DateFormatSymbols symbols = new DateFormatSymbols();
        for (String s : symbols.getWeekdays()) {
            if (s != null && !s.trim().isEmpty()) {
                panel.add(createCheckBox(s));
            }
        }
        setPopupComponent(this, panel);
    }

    private JCheckBox createCheckBox(String text) {
        final JCheckBox cb = new JCheckBox(text);
        cb.setHorizontalAlignment(SwingConstants.LEADING);
        cb.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                StringBuilder b = new StringBuilder();
                for (Component c : panel.getComponents()) {
                    if (c instanceof JCheckBox && ((JCheckBox) c).isSelected()) {
                        if (b.length() > 0) {
                            b.append(", ");
                        }
                        b.append(((JCheckBox) c).getText().substring(0, 3));
                    }
                }
                getModel().setSelectedItem(b.toString());
            }
        });
        return cb;
    }

    /**
     * Sets the custom component as popup component for the combo-box.
     * 
     * @param combo combo-box to get new popup component.
     * @param comp new popup component.
     */
    public static void setPopupComponent(JComboBox<?> combo, Component comp) {
        final ComboPopup popup = (ComboPopup) combo.getUI().getAccessibleChild(combo, 0);
        if (popup instanceof Container) {
            Container c = (Container) popup;
            c.removeAll();
            c.setLayout(new GridLayout(1, 1));
            c.add(comp);
            c.setPreferredSize(comp.getPreferredSize());
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frm = new JFrame("Test");
                frm.add(new ComboBoxTryout(), BorderLayout.NORTH);
                frm.setSize(250, 600);
                frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
                frm.setLocationRelativeTo(null);
                frm.setVisible(true);
            }
        });
    }
}

我可以打开组合框但是当我单击弹出窗口中的复选框时,组合框将关闭,但仍未选中复选框。但是,当我改变行

frm.add(new ComboBoxTryout(), BorderLayout.NORTH);

frm.add(new ComboBoxTryout(), BorderLayout.SOUTH);

一切正常:我可以更改复选框的状态,弹出窗口仍然可见!任何建议,如何让它适用于BorderLayout.NORTH

2 个答案:

答案 0 :(得分:1)

以下内容并未使用常规JComboBox,而是模仿其中一项,以便为您提供更多控制权。

此示例的灵感来自Creating Pop-Up Components 和您的代码。

它在按钮和文本字段上使用Popup组件和侦听器(textfield +按钮看起来像一个组合框,此部分在此示例中未进行视觉优化)来管理弹出窗口隐藏/显示。 / p>

import java.awt.Component;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.DateFormatSymbols;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.SwingConstants;

public class ButtonPopupSample {

    private final JTextField tField;
    private final JButton start;
    private final JPanel panel;
    private boolean popping = false;
    private Popup popup;

    ButtonPopupSample() {

        panel = new JPanel(new GridLayout(7, 1, 5, 5));
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        DateFormatSymbols symbols = new DateFormatSymbols();
        for (String s : symbols.getWeekdays()) {
            if (s != null && !s.trim().isEmpty()) {
                panel.add(createCheckBox(s));
            }
        }

        JFrame frame = new JFrame("Button Popup Sample");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        tField = new JTextField(20);
        tField.setEditable(false);
        start = new JButton("Pop");

        ActionListener actionListener = new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                popOrNot();

            }
        };
        start.addActionListener(actionListener);
        tField.addMouseListener(new MouseAdapter() {

            @Override
            public void mousePressed(final MouseEvent me) {

                popOrNot();

            }

        });
        JPanel contPan = new JPanel();

        contPan.add(tField);
        contPan.add(start);

        frame.setContentPane(contPan);
        frame.setSize(350, 250);
        frame.setVisible(true);
    }

    private JCheckBox createCheckBox(final String text) {
        final JCheckBox cb = new JCheckBox(text);
        cb.setHorizontalAlignment(SwingConstants.LEADING);
        cb.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                StringBuilder b = new StringBuilder();
                for (Component c : panel.getComponents()) {
                    if (c instanceof JCheckBox && ((JCheckBox) c).isSelected()) {
                        if (b.length() > 0) {
                            b.append(", ");
                        }
                        b.append(((JCheckBox) c).getText().substring(0, 3));
                    }
                }

                tField.setText(b.toString());

            }
        });
        return cb;
    }

    private void popOrNot() {

        if (popping) {
            popup.hide();
        } else {

            PopupFactory factory = PopupFactory.getSharedInstance();
            Point location = tField.getLocationOnScreen();
            popup = factory.getPopup(tField, panel, location.x, location.y + tField.getHeight());
            popup.show();

        }

        popping = !popping;
    }

    public static void main(final String args[]) {

        new ButtonPopupSample();
    }
}

答案 1 :(得分:1)

找到解决方案。奇怪的焦点改变事件提供了上述问题。因此必须防止对此事件的处理。

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.DateFormatSymbols;

import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.plaf.basic.ComboPopup;

@SuppressWarnings("serial")
public class ComboBoxTryout extends JComboBox {

    private JPanel panel;

    private boolean avoidFocusChange;

    /**
     * 
     */
    public ComboBoxTryout() {
        initPanel();
    }

    private void initPanel() {
        panel = new JPanel(new GridLayout(7, 1, 5, 5));
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        DateFormatSymbols symbols = new DateFormatSymbols();
        for (String s : symbols.getWeekdays()) {
            if (s != null && !s.trim().isEmpty()) {
                panel.add(createCheckBox(s));
            }
        }
        setPopupComponent(this, panel);
    }

    @Override
    protected void processFocusEvent(FocusEvent e) {
        if (avoidFocusChange && FocusEvent.FOCUS_LOST == e.getID() && panel.isAncestorOf(e.getOppositeComponent())) {
            System.out.println(e); // skip it
        } else {
            super.processFocusEvent(e);
        }
        avoidFocusChange = false;
    }

    private JCheckBox createCheckBox(String text) {
        final JCheckBox cb = new JCheckBox(text);
        cb.setHorizontalAlignment(SwingConstants.LEADING);
        cb.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                StringBuilder b = new StringBuilder();
                for (Component c : panel.getComponents()) {
                    if (c instanceof JCheckBox && ((JCheckBox) c).isSelected()) {
                        if (b.length() > 0) {
                            b.append(", ");
                        }
                        b.append(((JCheckBox) c).getText().substring(0, 3));
                    }
                }
                getModel().setSelectedItem(b.toString());
            }
        });
        cb.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                avoidFocusChange = SwingUtilities.isLeftMouseButton(e);
            }
        });
        return cb;
    }

    /**
     * Sets the custom component as popup component for the combo-box.
     * 
     * @param combo combo-box to get new popup component.
     * @param comp new popup component.
     */
    public static void setPopupComponent(JComboBox<?> combo, Component comp) {
        final ComboPopup popup = (ComboPopup) combo.getUI().getAccessibleChild(combo, 0);
        if (popup instanceof Container) {
            Container c = (Container) popup;
            c.removeAll();
            c.setLayout(new GridLayout(1, 1));
            c.add(comp);
            Dimension dim = comp.getPreferredSize();
            dim.width += 10; // need 10 px more width
            c.setPreferredSize(dim);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frm = new JFrame("Test");
                frm.add(new ComboBoxTryout(), BorderLayout.NORTH);
                frm.setSize(250, 600);
                frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
                frm.setLocationRelativeTo(null);
                frm.setVisible(true);
            }
        });
    }
}

如果有人找到更好的解决方案,请发帖!!!