我正在寻找一种方法来改变JComboBox弹出窗口的宽度。基本上,弹出框应该与最宽的组合框条目一样宽,而不是像目前的组合框那样宽。
我知道如何实现这一目标的唯一方法是创建一个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);
}
}
答案 0 :(得分:2)
使用GBC,您可以在容器中设置适当的高度和重量
正如@trashgod提到的下一种方法是使用PreferredSize
JComboBox.setPrototypeDisplayValue()
使用@camick的Combo Box Popup
必须使用派生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();
}
});
}
}
答案 1 :(得分:1)
这个hack似乎适用于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;
}
}