1

JComboBox のポップアップの幅を変更する方法を探しています。基本的に、ポップアップは、コンボボックスの現在の幅ではなく、最も幅の広いコンボボックス エントリが必要とする幅にする必要があります。

これを達成する方法を私が知っている唯一の方法は、ComboBoxUI のカスタム インスタンスを作成し、それを JComboBox に設定することです (サンプル コードは目標を示しています: Top Combobox はワイド ポップアップを表示し、Bottom はデフォルトの動作です)。ただし、これは ComboBox の UI を置き換えるため、一部の L&F では奇妙に見える場合があります (たとえば、WinXP Luna テーマでは、ComboBox は Classic テーマのように見えます)。

L&Fにとらわれない方法でこの動作を実現する方法はありますか?

public class CustomCombo extends JComboBox {

    final static class CustomComboUI extends BasicComboBoxUI {
        protected ComboPopup createPopup() {
            BasicComboPopup popup = new BasicComboPopup(comboBox) {
                @Override
                protected Rectangle computePopupBounds(int px, int py, int pw, int ph) {
                    return super.computePopupBounds(px, py, Math.max(
                            comboBox.getPreferredSize().width, pw), ph);
                }
            };
            popup.getAccessibleContext().setAccessibleParent(comboBox);
            return popup;
        }
    }

    {
        setUI(new CustomComboUI());
    }

    public static void main(String[] argv) {
        try {
            final String className = UIManager.getSystemLookAndFeelClassName();
            UIManager.setLookAndFeel(className);
        } catch (final Exception e) {
            // ignore
        }
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createGUI();    
            }
        });
    }

    public static void createGUI() {
        JComboBox combo1 = new CustomCombo();
        JComboBox combo2 = new JComboBox();
        JPanel panel = new JPanel();
        JFrame frame = new JFrame("Testframe");
        combo1.addItem("1 Short item");
        combo1.addItem("2 A very long Item name that should display completely in the popup");
        combo1.addItem("3 Another short one");
        combo2.addItem("1 Short item");
        combo2.addItem("2 A very long Item name that should display completely in the popup");
        combo2.addItem("3 Another short one");
        panel.setPreferredSize(new Dimension(30, 50));
        panel.setLayout(new GridBagLayout());
        GridBagConstraints gc;
        gc = new GridBagConstraints(0, 0, 1, 1, 1D, 0D, GridBagConstraints.WEST,
                GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0);
        panel.add(combo1, gc);
        gc = new GridBagConstraints(0, 1, 1, 1, 1D, 0D, GridBagConstraints.WEST,
                GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0);
        panel.add(combo2, gc);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());
        frame.add(panel, BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }

}
4

2 に答える 2

2
  1. GBCを使用することで、コンテナに適切な高さと重量を設定できます

  2. @trashgodが次に言及したように、次の方法はPreferredSize使用して設定することですJComboBox.setPrototypeDisplayValue()

  3. @camickr のCombo Box Popupを使用

  4. 派生したものJPopupを使用する必要があります。そうしないと簡単pack()に変更できませんDimension

.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;

public class ComboBoxExample extends JPanel implements ActionListener {
//http://stackoverflow.com/a/5058210/714968

    private static final long serialVersionUID = 1L;
    private JComboBox comboBox;

    public ComboBoxExample() {
        String[] petStrings = {"Select Pet", "Bird", "Cat", "Dog", "Rabbit", "Pig", "Other"};
        comboBox = new JComboBox(petStrings);
        comboBox.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
        add(comboBox, BorderLayout.PAGE_START);
        JFrame frame = new JFrame("ComboBoxExample");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(comboBox);
        frame.setName("ComboBoxExample");
        frame.setLocation(150, 150);
        frame.pack();
        frame.setVisible(true);
        Timer timer = new javax.swing.Timer(2000, this);
        timer.start();
    }

    public void actionPerformed(ActionEvent e) {
        comboBox.showPopup();
        Object child = comboBox.getAccessibleContext().getAccessibleChild(0);
        BasicComboPopup popup = (BasicComboPopup) child;
        popup.setName("BasicComboPopup");
        JList list = popup.getList();
        Container c = SwingUtilities.getAncestorOfClass(JScrollPane.class, list);
        JScrollPane scrollPane = (JScrollPane) c;
        Dimension size = scrollPane.getSize();
        if (size.width > 30) {
            size.width -= 5;
        }
        scrollPane.setPreferredSize(size);
        scrollPane.setMaximumSize(size);
        Dimension popupSize = popup.getSize();
        popupSize.width = size.width;
        Component parent = popup.getParent();
        parent.setSize(popupSize);
        parent.validate();
        parent.repaint();
        Window mainFrame = SwingUtilities.windowForComponent(comboBox);
        //Returns the first Window ancestor of c, or null if c is not contained inside a Window.
        System.out.println(mainFrame.getName());
        Window popupWindow = SwingUtilities.windowForComponent(popup);
        System.out.println(popupWindow.getName());
        Window popupWindowa = SwingUtilities.windowForComponent(c);
        System.out.println(popupWindowa.getName());

        Window mainFrame1 = SwingUtilities.getWindowAncestor(comboBox);
        //Returns the first Window ancestor of c, or null if c is not contained inside a Window.
        System.out.println(mainFrame1.getName());
        Window popupWindow1 = SwingUtilities.getWindowAncestor(popup);
        System.out.println(popupWindow1.getName());

        Component mainFrame2 = SwingUtilities.getRoot(comboBox);
        //Returns the root component for the current component tree.
        System.out.println(mainFrame2.getName());
        Component popupWindow2 = SwingUtilities.getRoot(popup);
        System.out.println(popupWindow2.getName());
        //  For heavy weight popups you need always to pack() for the window
        if (popupWindow != mainFrame) {
            popupWindow.pack();
        }
    }

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

            public void run() {
                ComboBoxExample comboBoxExample = new ComboBoxExample();
            }
        });
    }
}
于 2012-07-11T15:15:46.727 に答える
1

このハックはJava6/7で機能するようです。基本的にgetSize()をオーバーライドして、ポップアップUI内からメソッドが呼び出されたかどうかを検出します。それが検出された場合、それはコンボボックスの現在のサイズにあります。次に、ポップアップは偽のサイズ値に従ってサイズを決定します。

検出コードは残酷で(呼び出しスタックを調べます)、呼び出し元のクラスの名前にComboPopupが含まれているという前提に基づいて構築されています。一部のUI実装では状態の検出に失敗する場合があります。その場合、デフォルトの動作が保持されます。

public class PopupHackComboBox extends JComboBox {

    // --------------------------------------------------------------
    // ---
    // --- Hack to get control of combobox popup size
    // ---
    // --------------------------------------------------------------
    /**
     * Gets the width the combo's popup should use.
     * 
     * Can be overwritten to return any width desired.
     */
    public int getPopupWidth() {
        final Dimension preferred = getPreferredSize();
        return Math.max(getWidth(), preferred.width);
    }

    @SuppressWarnings("deprecation")
    @Override
    public Dimension size() {
        return getSize((Dimension) null);
    }

    @Override
    public Dimension getSize() {
        return getSize((Dimension) null);
    }

    @Override
    public Dimension getSize(final Dimension dimension) {
        // If the method was called from the ComboPopup,
        // simply lie about the current size of the combo box.
        final int width = isCalledFromComboPopup() ? getPopupWidth() : getWidth();
        if (dimension == null) {
            return new Dimension(width, getHeight());
        }
        dimension.width = width;
        dimension.height = getHeight();
        return dimension;
    }

    /**
     * Hack method to determine if called from within the combo popup UI.
     */
    public boolean isCalledFromComboPopup() {
        try {
            final Throwable t = new Throwable();
            t.fillInStackTrace();
            StackTraceElement[] st = t.getStackTrace();
            // look only at top 5 elements of call stack
            int max = Math.min(st.length, 5);
            for (int i=0; i<max; ++i) {
                final String name = st[i].getClassName();
                if (name != null && name.contains("ComboPopup")) {
                    return true;
                }
            }
        } catch (final Exception e) {
            // if there was a problem, assume not called from combo popup
        }
        return false;
    }

}
于 2012-07-12T13:48:26.327 に答える