How to prevent JComboBox from becoming unresponsive when using a custom ListCellRenderer

前端 未结 2 1542
梦谈多话
梦谈多话 2020-12-12 01:41

I am making a font chooser using JComboBox and a custom ListCellRenderer. I want the JComboBox to display all available fonts, with ea

2条回答
  •  醉话见心
    2020-12-12 02:14

    What happens is that the combo's internals try to find the preferred size dynamically. For doing so, it loops through all items in the list, feeds the renderer with the items to measure the rendering component's preferred size.

    You can prevent that by setting a prototypeValue for measuring, then the size is measured once using that prototype

     comboBox.setPrototypeDisplayValue(sampleFont);
    

    Edit: as @Boro detected, that's not enough - it sets the prototype for the comboBox itself only, not for the list in the popup (as it should, how crazily buggy can that ... possibly be). To hack around, we have to manually set it, here's a code snippet to play with

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

    Custom ListCellRenderer (slightly changed, to expect items of type 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;
        }
    }
    

提交回复
热议问题