改变JComboBox弹出尺寸而不会产生令人不安的外观和感觉?

我正在寻找一种方法来改变JComboBox弹出窗口的宽度。 基本上,弹出框应该与最宽的combobox条目一样宽,而不是像目前的combobox那样宽。

我知道如何实现这一目标的唯一方法是创建一个ComboBoxUI的自定义实例并在JComboBox上设置它(示例代码演示了目标:Top Combobox显示宽弹出窗口,Bottom是默认行为)。 然而,由于这取代了ComboBox的UI,在一些L&F上可能看起来很奇怪(例如,使用WinXP Luna主题,ComboBox看起来像经典主题)。

有没有办法以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); } } 

  1. 通过使用GBC,您可以在容器中设置适当的高度和重量

  2. as @trashgod提到的下一种方法是使用JComboBox.setPrototypeDisplayValue()设置PreferredSize

  3. 使用@camickr的combobox弹出窗口

  4. 对于派生的JPopup必须使用pack()否则太难以改变其Dimension easilly

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

这个hack似乎适用于Java6 / 7。 它基本上覆盖了getSize()以检测是否从弹出UI中调用该方法。 如果检测到它,那就是combobox的当前大小。 弹出窗口然后根据伪造的大小值确定其大小。

检测代码是残酷的(它查看调用堆栈)并围绕调用类的名称包含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