如何在使用自定义ListCellRenderer时防止JComboBox无响应

我正在使用JComboBox和自定义ListCellRenderer制作字体选择器。 我希望JComboBox显示所有可用的字体,每个字体名称都以自己的字体显示。 我目前使用大约500种字体。

提供此function的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在程序执行期间变得无响应。 第一次单击combobox以显示列表时,列表加载需要几秒钟。 第二次单击,立即显示列表。

如果有人评论该行

 setFont(new Font((String) value, tempFont.getStyle(),tempFont.getSize())); 

,combobox工作得很好。

如何防止这种反应迟钝呢?

会发生什么是组合的内部尝试动态地找到首选大小。 为此,它循环遍历列表中的所有项目,向渲染器提供项目以测量渲染组件的首选大小。

您可以通过设置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; } } 

@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; } } }