我正在使用JComboBox
和自定义ListCellRenderer
制作字体选择器。我想要
JComboBox
显示所有可用字体,每个字体名称以其自己的字体显示。我目前使用大约500种字体。
提供此功能的ListCellRenerer
示例:
private class ComboBoxRenderer extends JLabel implements ListCellRenderer {
private JLabel label = new JLabel("Test");
@Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
Font tempFont = label.getFont();
setFont(new Font((String) value, tempFont.getStyle(),
tempFont.getSize()));
setText((String) value);
return this;
}
}
问题在于,使用此渲染器时,JComboBox
在程序执行期间无响应。第一次单击组合框以显示列表时,列表加载需要几秒钟。第二次单击,立即显示列表。
如果有人评论该行
setFont(new Font((String) value, tempFont.getStyle(),tempFont.getSize()));
,组合框效果很好。
如何防止这种无反应?
答案 0 :(得分:3)
组合的内部结构会尝试动态查找首选大小。为此,它遍历列表中的所有项目,向渲染器提供项目以测量渲染组件的首选大小。
您可以通过设置prototypeValue进行测量来防止这种情况,然后使用该原型测量尺寸
comboBox.setPrototypeDisplayValue(sampleFont);
编辑:正如@Boro检测到的那样,这还不够 - 它只为comboBox本身设置原型,而不是弹出窗口中的列表(因为它应该如此,可能是多么疯狂的错误......)。要破解,我们必须手动设置它,这是一个与一起使用的代码片段
public class ComboWithPrototype {
private JComponent createContent() {
final Font[] systemFonts = GraphicsEnvironment
.getLocalGraphicsEnvironment().getAllFonts();
final JComboBox box = new JComboBox();
box.setRenderer(new ComboBoxRenderer());
box.setPrototypeDisplayValue(systemFonts[0]);
Accessible a = box.getUI().getAccessibleChild(box, 0);
if (a instanceof javax.swing.plaf.basic.ComboPopup) {
JList popupList = ((javax.swing.plaf.basic.ComboPopup) a).getList();
// route the comboBox' prototype to the list
// should happen in BasicComboxBoxUI
popupList.setPrototypeCellValue(box.getPrototypeDisplayValue());
}
Action action = new AbstractAction("set model") {
@Override
public void actionPerformed(ActionEvent e) {
box.setModel(new DefaultComboBoxModel(systemFonts));
}
};
JComponent panel = new JPanel(new BorderLayout());
panel.add(box);
panel.add(new JButton(action), BorderLayout.SOUTH);
return panel;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new ComboWithPrototype().createContent());
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
});
}
自定义ListCellRenderer(略有改动,期望Font类型的项目)
private class ComboBoxRenderer extends DefaultListCellRenderer {
private Font baseFont = new JLabel("Test").getFont();
@Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected,
cellHasFocus);
if (value instanceof Font) {
Font font = (Font) value;
setFont(new Font(font.getName(), baseFont.getStyle(), baseFont.getSize()));
setText(font.getName());
}
return this;
}
}
答案 1 :(得分:-1)
@kleopatra比你注意到我但setPrototypeDisplayValue
看起来像懒惰的选择,我的讽刺
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
public class SystemFontDisplayer extends JFrame {
private static final long serialVersionUID = 1L;
private JComboBox fontsBox;
public SystemFontDisplayer() {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontFamilyNames = ge.getAvailableFontFamilyNames();
fontsBox = new JComboBox(fontFamilyNames);
fontsBox.setSelectedItem(0);
fontsBox.setRenderer(new ComboRenderer(fontsBox));
fontsBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
final String fontName = fontsBox.getSelectedItem().toString();
fontsBox.setFont(new Font(fontName, Font.PLAIN, 16));
}
}
});
fontsBox.setSelectedItem(0);
fontsBox.getEditor().selectAll();
add(fontsBox, BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(400, 60));
setLocation(200, 105);
pack();
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
fontsBox.setPopupVisible(true);
fontsBox.setPopupVisible(false);
}
});
setVisible(true);
}
public static void main(String arg[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
SystemFontDisplayer systemFontDisplayer = new SystemFontDisplayer();
}
});
}
private class ComboRenderer extends BasicComboBoxRenderer {
private static final long serialVersionUID = 1L;
private JComboBox comboBox;
final DefaultListCellRenderer defaultRenderer = new DefaultListCellRenderer();
private int row;
private ComboRenderer(JComboBox fontsBox) {
comboBox = fontsBox;
}
private void manItemInCombo() {
if (comboBox.getItemCount() > 0) {
final Object comp = comboBox.getUI().getAccessibleChild(comboBox, 0);
if ((comp instanceof JPopupMenu)) {
final JList list = new JList(comboBox.getModel());
final JPopupMenu popup = (JPopupMenu) comp;
final JScrollPane scrollPane = (JScrollPane) popup.getComponent(0);
final JViewport viewport = scrollPane.getViewport();
final Rectangle rect = popup.getVisibleRect();
final Point pt = viewport.getViewPosition();
row = list.locationToIndex(pt);
}
}
}
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (list.getModel().getSize() > 0) {
manItemInCombo();
}
final JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, row, isSelected, cellHasFocus);
final Object fntObj = value;
final String fontFamilyName = (String) fntObj;
setFont(new Font(fontFamilyName, Font.PLAIN, 16));
return this;
}
}
}