简体   繁体   中英

Colored jcombobox with colored items and focus

i am trying to make a colored dropdown list with colored items (please see code below). The color is getting applied after the combobox loses focus.

is this correct behaviour?

how can i get the foreground and/or the background color to change when the combobox has the focus?

thanks

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
public class DropDown {
    enum Colors {
        red(Color.red), orange(Color.orange), green(Color.green), yellow(Color.yellow), blue(Color.blue);
        Colors(Color color) {
            this.color = color;
        }
        static String[] listModel() {
            java.util.List<Colors> values = Arrays.asList(values());
            String s = values.toString().replaceAll(" ", "");
            return s.substring(1, s.length() - 1).split(",");
        }
        final Color color;
    }
    static class ColoredCellRenderer implements ListCellRenderer {
        protected DefaultListCellRenderer defaultRenderer = new DefaultListCellRenderer();
        private final static Dimension preferredSize = new Dimension(0, 20);
        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
                boolean cellHasFocus) {
            JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index, isSelected,
                    cellHasFocus);
            if (index != -1) if (value instanceof String) renderer.setForeground(Colors.valueOf((String) value).color);
            else
                System.out.println("not a string");
            else
                System.out.println("in getListCellRendererComponent, index=-1");
            renderer.setPreferredSize(preferredSize);
            return renderer;
        }
    }
    private static JComboBox addLabeledComboBox(Container c, String label, String[] model) {
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
        Border border = BorderFactory.createLineBorder(Color.green, 1);
        panel.setBorder(border);
        JLabel l = new JLabel(label);
        panel.add(l);
        final JComboBox comboBox = new JComboBox(model);
        comboBox.setName(label);
        l.setLabelFor(comboBox);
        comboBox.setRenderer(new ColoredCellRenderer());
        panel.add(comboBox);
        c.add(panel);
        return comboBox;
    }
    private static void addContent(Container c) {
        c.setLayout(new BoxLayout(c, BoxLayout.Y_AXIS));
        ActionListener actionListener = new ActionListener() {
            @Override public void actionPerformed(ActionEvent e) {
                if (e.getSource() instanceof JComboBox) {
                    final JComboBox comboBox = (JComboBox) e.getSource();
                    Object selected = comboBox.getSelectedItem();
                    String string = (String) selected;
                    Color color = Colors.valueOf(string).color;
                    int i = comboBox.getSelectedIndex();
                    if (i != -1) {
                        System.out.println("comboBox " + comboBox.getName() + " " + color);
                        comboBox.setForeground(color);
                        System.out.println(comboBox.hasFocus());
                        // comboBox.repaint();
                    } else
                        System.out.println("in actionListener selected=" + selected);
                } else
                    System.out.println("in actionListener selected is not a comboBox");
            }
        };
        c.add(new JButton("button"));
        for (int i = 0; i < 2; i++) {
            JPanel panel = new JPanel();
            Border border = BorderFactory.createLineBorder(Color.red, 1);
            panel.setBorder(border);
            panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
            panel.add(new JLabel("label " +(i+1)));
            JComboBox comboBox = addLabeledComboBox(panel, "" + (i + 1), Colors.listModel());
            comboBox.addActionListener(actionListener);
            comboBox.setSelectedIndex(0); // select something
            c.add(panel);
        }
    }
    private static void createAndShowGUI() {
        JFrame frame = new JFrame("Colored JComboBox");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        addContent(frame.getContentPane());
        frame.pack();
        frame.setVisible(true);
    }
    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            @SuppressWarnings("synthetic-access") public void run() {
                createAndShowGUI();
            }
        });
    }
}

please split Editor and Renderer, and add to DefaultListCellRenderer these two methods

  • cellHasFocus

  • isSelected

example

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.LineBorder;
import javax.swing.event.EventListenerList;

class ColorComboBoxEditor1 implements ComboBoxEditor {

    final protected JLabel editor;
    //final protected JButton editor;
    private EventListenerList listenerList = new EventListenerList();

    ColorComboBoxEditor1(Color initialColor) {
        editor = new JLabel("");
        /*editor = new JButton("");
        editor.setBackground(initialColor);
        ActionListener actionListener = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                Color currentBackground = editor.getBackground();
                Color color = JColorChooser.showDialog(editor, "Color Chooser", currentBackground);
                if ((color != null) && (currentBackground != color)) {
                    //editor.setBackground(color);
                    editor.setForeground(color);
                    fireActionEvent(color);
                }
            }
        };
        editor.addActionListener(actionListener);*/
    }

    @Override
    public void addActionListener(ActionListener l) {
        listenerList.add(ActionListener.class, l);
    }

    @Override
    public Component getEditorComponent() {
        return editor;
    }

    @Override
    public Object getItem() {
        return editor.getBackground();
    }

    @Override
    public void removeActionListener(ActionListener l) {
        listenerList.remove(ActionListener.class, l);
    }

    @Override
    public void selectAll() {
    }

    @Override
    public void setItem(Object newValue) {
        if (newValue instanceof Color) {
            Color color = (Color) newValue;
            //editor.setBackground(color);
            editor.setForeground(color);
            editor.setText(String.valueOf(newValue));
        } else {
            try {
                Color color = Color.decode(newValue.toString());
                //editor.setBackground(color);
                editor.setForeground(color);
                editor.setText(String.valueOf(newValue));
            } catch (NumberFormatException e) {
            }
        }
    }

    protected void fireActionEvent(Color color) {
        Object listeners[] = listenerList.getListenerList();
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == ActionListener.class) {
                ActionEvent actionEvent = new ActionEvent(editor, ActionEvent.ACTION_PERFORMED, color.toString());
                ((ActionListener) listeners[i + 1]).actionPerformed(actionEvent);
            }
        }
    }
}

class ColorCellRenderer1 implements ListCellRenderer {

    private DefaultListCellRenderer defaultRenderer = new DefaultListCellRenderer();
    // Width doesn't matter as the combo box will size
    private final static Dimension preferredSize = new Dimension(0, 20);

    @Override
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
        JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
        if (value instanceof Color) {
            //renderer.setBackground((Color) value);
            renderer.setBackground(Color.white);
            renderer.setForeground((Color) value);
        }
        if (cellHasFocus || isSelected) {
            renderer.setBorder(new LineBorder(Color.DARK_GRAY));
        } else {
            renderer.setBorder(null);
        }
        renderer.setPreferredSize(preferredSize);
        return renderer;
    }
}

class ColorComboBoxEditorRendererDemo1 {

    public ColorComboBoxEditorRendererDemo1() {
        Color colors[] = {Color.BLACK, Color.BLUE, Color.GREEN, Color.RED, Color.WHITE, Color.YELLOW};
        JFrame frame = new JFrame("Color JComboBox");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final JComboBox comboBox = new JComboBox(colors);
        comboBox.setEditable(true);
        comboBox.setRenderer(new ColorCellRenderer1());
        Color color = (Color) comboBox.getSelectedItem();
        ComboBoxEditor editor = new ColorComboBoxEditor1(color);
        comboBox.setEditor(editor);
        frame.add(comboBox, BorderLayout.NORTH);
        final JLabel label = new JLabel();
        label.setOpaque(true);
        label.setBackground((Color) comboBox.getSelectedItem());
        frame.add(label, BorderLayout.CENTER);
        ActionListener actionListener = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                Color selectedColor = (Color) comboBox.getSelectedItem();
                label.setBackground(selectedColor);
            }
        };
        comboBox.addActionListener(actionListener);
        frame.setSize(300, 200);
        frame.setVisible(true);
    }

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                ColorComboBoxEditorRendererDemo1 cCBERD1 = new ColorComboBoxEditorRendererDemo1();
            }
        });
    }
}

Here is another approach:

//JList#setSelectionForeground(...) version
static class ColoredCellRenderer implements ListCellRenderer {
  protected DefaultListCellRenderer defaultRenderer = new DefaultListCellRenderer();
  private final Color selectionBackground = new Color(240,200,200);
  public Component getListCellRendererComponent(
      JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
    Color fgc = Colors.valueOf((String)value).color;
    if(index<0) {
      //comboBox.setForeground(fgc); //Windows, CDE/Motif Look & Feel
      list.setSelectionForeground(fgc);
      list.setSelectionBackground(selectionBackground);
    }
    JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(
        list, value, index, isSelected, cellHasFocus);
    if (index != -1) {
      renderer.setForeground(fgc);
    }
    return renderer;
  }
}

//html version
static class ComboHtmlRenderer extends DefaultListCellRenderer {
  private final Color selectionBackground = new Color(240,200,200);
  @Override public Component getListCellRendererComponent(
      JList list, Object value, int index, boolean isSelected, boolean hasFocus) {
    Color fgc = Colors.valueOf((String)value).color;
    if(index<0) {
      list.setSelectionBackground(selectionBackground);
    }
    JLabel l = (JLabel)super.getListCellRendererComponent(
                 list, value, index, isSelected, hasFocus);
    l.setText("<html><font color="+hex(fgc)+">"+value);
    l.setBackground(isSelected?selectionBackground:list.getBackground());
    return l;
  }
  private static String hex(Color c) {
    return String.format("#%06x", c.getRGB()&0xffffff);
  }
}

The foreground text colors in your example seem to work correctly. Certain other colors are managed by the component's UI delegate for a given Look & Feel, as shown below. Note that the delegate may choose to apply a color in a way that preserves contrast in various states, eg selected, enabled, etc. You can learn more in the article UIManager Defaults .

public static void main(String[] args) {
    UIManager.put("ComboBox.background", new ColorUIResource(Color.lightGray));
    UIManager.put("ComboBox.selectionBackground", new ColorUIResource(Color.magenta));
    UIManager.put("ComboBox.selectionForeground", new ColorUIResource(Color.yellow));
    javax.swing.SwingUtilities.invokeLater(new Runnable() {

        @SuppressWarnings("synthetic-access")
        public void run() {
            createAndShowGUI();
        }
    });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM