2011-05-05 56 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