2011-05-05 26 views
2

我正在使用JComboBox和自定义ListCellRenderer制作字体选择器。我想 JComboBox显示所有可用的字体,每个字体名称显示在其自己的字体中。我目前使用大约500种字体。如何防止使用自定义ListCellRenderer时JComboBox无响应

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

您可能想尝试创建一个'字体'对象的缓存。用于在90年代缓存'Font'和'FontMetric'。你可以为每个'Font'创建一个'JLabel'。 – 2011-05-05 10:45:48

+0

但用测试'if(isSelected){'或'cellHasFocus' – mKorbel 2011-05-05 10:45:50

+0

只注意到你说它只是第一次慢,而且你正在使用500字体。我猜加载500个字体是一项相当艰巨的任务。 – 2011-05-05 10:49:02

回答

3

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

您可以防止通过设置用于测量prototypeValue,那么尺寸测量一次使用原型

comboBox.setPrototypeDisplayValue(sampleFont); 

编辑:作为@Boro检测,这还不够 - 这台样机的组合框本身只是,不是在弹出列表中(因为它应该,多么疯狂的马车......可能是)。破解身边,我们必须手动设置,这里有一个代码段与

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玩(微变,期望类型字体的项目)为

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

@kleopatra你可以显示一个例子如何使用它,因为我正在尝试使用它,而不是运气。在安装渲染器之前或之后总是有延迟独立设置。 'Font [] opts = new Font [] {....}; \t \t \t \t JComboBox p = new JComboBox(opts); \t \t \t \t \t \t \t \t p。setRenderer(new ComboBoxRenderer()); \t \t \t \t p.setPrototypeDisplayValue(largestFont);'如果需要,我可以发布SSCCE,但我更改了渲染的主体。 – Boro 2011-05-05 11:10:47

+0

@Boro -f *,你说得对,设置该属性似乎不够:它阻止了通过所有值来循环调整_combo_本身,但仍显示弹出窗口时循环... – kleopatra 2011-05-05 11:49:41

+0

@kleopatra是啊这就是我所经历的。 – Boro 2011-05-05 11:52:13

-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; 
     } 
    } 
} 
+0

-1(失败)试图重新发明车轮。阅读setPrototypeDisplayVaule的api文档,了解它的存在是否正是它在这里需要的原因,也就是说_not_不会循环遍历所有元素,只是为了得到尺寸提示 – kleopatra 2011-05-06 13:53:31

+0

另一个-1来完成未完成的任务(如不完整列表) :a)让渲染器引用目标组合框b)在每次调用getRendererComponent时创建一个JList,c)在每次调用时配置一个未使用的渲染器...... – kleopatra 2011-05-06 14:21:46

+0

@ kleopatra :-)此渲染器在JList中没有Loop的情况下工作: - ),关于我的懒惰删除调用manItemInCombo(); – mKorbel 2011-05-06 15:53:41