Fiddle Freak
Fiddle Freak

Reputation: 2041

custom JComboBoxRenderer change background color depending on text selection

Updated Question

Part 1: Originally, I was first going to remove the highlight selection after an item has been selected (because this was messing up the background color for the selection made). I saw this could be done from here > Delete highlighting in JComboBox - (Solved)

Part 2: Now... I'm trying to have it identify the selected text, and change the background color (when selected, and not when mouse hovers over item in list) depending on which text was selected. It does change the color to match the text, however when selecting another object (de-selecting the combobox), the background color changes back to the default - (Solved - See my solution)

Part 3: Also, I would like to have the background color show for the text in the dropdown list (while the mouse is not being hovered over it). - (Solved)

Here is the code...

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class ComboBoxRenderer extends JLabel implements ListCellRenderer
{
    private Color selectionBackgroundColor;
    private DefaultListCellRenderer dlcr = new DefaultListCellRenderer();

    // Constructor
    public ComboBoxRenderer()
    {
        setOpaque(true);
        setHorizontalAlignment(CENTER);
        setVerticalAlignment(CENTER);
        selectionBackgroundColor = this.dlcr.getBackground(); // Have to set a color, else a compiler error will occur
    }

    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)
    {
        // Set the list background color to default color (default color will show once selection is made)
        setBackground(list.getBackground());
        Color mouseHoverHighlight = Color.LIGHT_GRAY;
        setText((String)value);

        // Check which item is selected
        if(isSelected)
        {
            // Set background color of the item your cursor is hovering over to the original background color
            setBackground(mouseHoverHighlight);
        }
        else
        {
            // Set background to specific color depending on text value
            String selectedTextInDropdownList = getText();
            if (selectedTextInDropdownList.equals("SelectedTextOne")) {
                setBackground(Color.GREEN);
            } else if (selectedTextInDropdownList.equals("SelectedTextTwo")) {
                setBackground(Color.RED);
            }
        }
        String selectedText = getText();
        if (selectedText.equals("SelectedTextOne")){
            list.setSelectionBackground(Color.GREEN);
        } else if (selectedText.equals("SelectedTextTwo")){
            list.setSelectionBackground(Color.RED);
        } else {
            list.setSelectionBackground(this.dlcr.getBackground());
        }

        return this;
    }
}

Edit: Also... Here is the code to drum up a GUI so you can see how it is behaving...

Edit2: Edited code to compile without background shown in images

import org.apache.commons.lang3.ArrayUtils;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.metal.MetalComboBoxButton;

public class TestGui extends JFrame {
    private final String[] guiCharSelDefault = {"---  Select Character ---"};
    private final String[] characters = {"SelectedTextOne", "SelectedTextTwo", "SelectedTextThree", "SelectedTextFour"};
    private final String[] guiCharSel = (String[]) ArrayUtils.addAll(guiCharSelDefault, characters);
    private JComboBox charCombo = createStandardCombo(guiCharSel);
    private JPanel topFrame = createTopFrame();
    private JScrollPane topFrameScroll = createTopScrollPane();
    private JPanel centerFrame = createCenterFrame();

    //**************************************************************************************
    // Constructor

    TestGui(){
        add(topFrameScroll, BorderLayout.NORTH);
        add(centerFrame, BorderLayout.CENTER);

        setSize(800,600);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    //**************************************************************************************
    // Support Methods
    private static GridBagConstraints setGbc(int gridx, int gridy, int gridWidth, int gridHeight, int ipadx, int ipady, String anchorLocation, double weightx, double weighty, Insets insets){
        GridBagConstraints gbc = new GridBagConstraints();

        if (anchorLocation.toUpperCase().equals("NORTHWEST")){
            gbc.anchor = GridBagConstraints.NORTHWEST;
        } else if (anchorLocation.toUpperCase().equals("NORTH")){
            gbc.anchor = GridBagConstraints.NORTH;
        } else if (anchorLocation.toUpperCase().equals("NORTHEAST")){
            gbc.anchor = GridBagConstraints.NORTHEAST;
        } else if (anchorLocation.toUpperCase().equals("WEST")){
            gbc.anchor = GridBagConstraints.WEST;
        } else if (anchorLocation.toUpperCase().equals("EAST")){
            gbc.anchor = GridBagConstraints.EAST;
        } else if (anchorLocation.toUpperCase().equals("SOUTHWEST")){
            gbc.anchor = GridBagConstraints.SOUTHWEST;
        } else if (anchorLocation.toUpperCase().equals("SOUTH")){
            gbc.anchor = GridBagConstraints.SOUTH;
        } else if (anchorLocation.toUpperCase().equals("SOUTHEAST")){
            gbc.anchor = GridBagConstraints.SOUTHEAST;
        } else {
            gbc.anchor = GridBagConstraints.CENTER;
        }

        gbc.gridx = gridx; // column
        gbc.gridy = gridy; // row
        gbc.gridwidth = gridWidth; // number of columns
        gbc.gridheight = gridHeight; // number of rows
        gbc.ipadx = ipadx; // width of object
        gbc.ipady = ipady; // height of object
        gbc.weightx = weightx; // shifts rows to side of set anchor
        gbc.weighty = weighty; // shifts columns to side of set anchor
        gbc.insets = insets; // placement inside cell
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.fill = GridBagConstraints.VERTICAL;

        return gbc;
    }

    private Insets setInsets(int top, int left, int bottom, int right){
        Insets insets = new Insets(top,left,bottom,right);
        return insets;
    }

    //**************************************************************************************
    // Interactive Object Methods

    private JComboBox createStandardCombo(String[] defaultValues){
        JComboBox comboBox = new JComboBox(defaultValues);
        ComboBoxRenderer cbr = new ComboBoxRenderer();
        DefaultListCellRenderer dlcr = new DefaultListCellRenderer();
        dlcr.setHorizontalTextPosition(SwingConstants.CENTER);
        comboBox.setRenderer(cbr);
        comboBox.setPrototypeDisplayValue("X" + guiCharSelDefault + "X");
        return comboBox;
    }

    //**************************************************************************************
    // Object Action Methods

    private void setComboBoxColorBackgroundWithMetalArrow(Color color){
        int componentCount = charCombo.getComponentCount();
        for (int i = 0; i < componentCount; i++) {
            Component component = charCombo.getComponent(i);
            if (component instanceof MetalComboBoxButton) {
                MetalComboBoxButton metalComboBoxButton =
                        (MetalComboBoxButton) component;
                Icon comboIcon = metalComboBoxButton.getComboIcon();
                BufferedImage bufferedImage =
                        new BufferedImage(
                                comboIcon.getIconWidth(),
                                comboIcon.getIconHeight(),
                                BufferedImage.TYPE_INT_ARGB);
                comboIcon.paintIcon(
                        metalComboBoxButton, bufferedImage.getGraphics(), 0, 0);
            }
        }
    }

    private void setCharComboAction(){
        charCombo.addActionListener(
                new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        String charName = ((JComboBox)(e.getSource())).getSelectedItem().toString();
                        if (!(charName.equals(guiCharSelDefault[0]))){
                            DefaultListCellRenderer dlcr = new DefaultListCellRenderer();
                            DefaultComboBoxModel model = new DefaultComboBoxModel(characters);
                            model.setSelectedItem(charName);
                            charCombo.setModel(model);
                        }
                    }
                }
        );
    }

    //**************************************************************************************
    // Panel Methods

    private JPanel createTopFrame(){
        JPanel pnl = new JPanel();

        pnl.setLayout(new GridBagLayout());

        setCharComboAction();
        pnl.add(charCombo, setGbc(0,0, 1,1, 0,0, "WEST", 0, 0, setInsets(0, 10, 0, 0)));
        JButton button = new JButton("Button");
        pnl.add(button, setGbc(0,1, 1,1, 0,0, "WEST", 0, 0, setInsets(0, 10, 0, 0)));

        pnl.setOpaque(false);
        return pnl;
    }

    private JScrollPane createTopScrollPane(){
        JScrollPane scrollPane = new JScrollPane();
        Border raisedBevel = BorderFactory.createRaisedBevelBorder();
        Border lineBorder = BorderFactory.createMatteBorder(2, 2, 2, 2, new Color(224,224,224));
        Border loweredBevel = BorderFactory.createLoweredBevelBorder();
        Border compoundSetup = BorderFactory.createCompoundBorder(raisedBevel, lineBorder);
        Border compoundFinal = BorderFactory.createCompoundBorder(compoundSetup, loweredBevel);

        scrollPane.setBorder(compoundFinal);
        scrollPane.setOpaque(false);
        scrollPane.getViewport().setOpaque(false);
        scrollPane.getViewport().setView(topFrame);
        return scrollPane;
    }

    private JPanel createCenterFrame() {
        JPanel pnl = new JPanel();
        Border raisedBevel = BorderFactory.createRaisedBevelBorder();
        Color lineColor = new Color(224, 224, 224);
        Border lineBorder = BorderFactory.createMatteBorder(5, 5, 5, 5, lineColor);
        Border loweredBevel = BorderFactory.createLoweredBevelBorder();
        Border compoundSetup = BorderFactory.createCompoundBorder(raisedBevel, lineBorder);
        Border compoundFinal = BorderFactory.createCompoundBorder(compoundSetup, loweredBevel);
        TitledBorder topFrameTitle = BorderFactory.createTitledBorder(compoundFinal, "Stuff");
        topFrameTitle.setTitleJustification(TitledBorder.CENTER);

        pnl.setBorder(topFrameTitle);
        pnl.setLayout(new GridBagLayout());

        pnl.setOpaque(false);
        return pnl;
    }

    //**************************************************************************************

    public static void main(String[] args) {

        new TestGui();
    }
}

Steps to reproduce issue:

Click the "select character" combobox

enter image description here

Click the "selectedTextOne" item in the list. Note: "selectedTextThree" is only a different color because my mouse is hovered over it. This is behaving properly.

enter image description here

Click the button. Note: the button doesn't actually do anything, we just do this so the combo box is no longer selected.

enter image description here

Notice how the combo box changes from green to gray (we want it to stay green).

enter image description here

Anyone have any idea how to make sure the background color(in the combo box itself and not the list) is set for the color of the text value?

Upvotes: 0

Views: 1793

Answers (2)

Fiddle Freak
Fiddle Freak

Reputation: 2041

I finally found the right combination. Here is the code...

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class ComboBoxRenderer extends JLabel implements ListCellRenderer
{
    private Color selectionBackgroundColor;
    private Color defaultBackgroundColor;
    private DefaultListCellRenderer dlcr = new DefaultListCellRenderer();

    // Constructor
    public ComboBoxRenderer()
    {
        setOpaque(true);
        setHorizontalAlignment(CENTER);
        setVerticalAlignment(CENTER);
        defaultBackgroundColor = this.dlcr.getBackground(); // Have to set a color, else a compiler error will occur
    }

    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)
    {
        // Set the list background color to default color (default color will show once selection is made)
        selectionBackgroundColor = null;
        Color mouseHoverHighlight = Color.LIGHT_GRAY;
        setText((String)value);
        // Check which item is selected
        if(isSelected)
        {
            // Set background color of the item your cursor is hovering over to the original background color
            String selectedText = getText();
            if (selectedText.equals("SelectedTextOne")){
                list.setSelectionBackground(Color.GREEN);
            } else if (selectedText.equals("SelectedTextTwo")){
                list.setSelectionBackground(Color.RED);
            } else {
                list.setSelectionBackground(defaultBackgroundColor);
            }
            setBackground(mouseHoverHighlight);
        }
        else
        {
            // Set background to specific color depending on text value
            String selectedTextInDropdownList = getText();
            if (selectedTextInDropdownList.equals("SelectedTextOne")) {
                selectionBackgroundColor = Color.GREEN;
            } else if (selectedTextInDropdownList.equals("SelectedTextTwo")) {
                selectionBackgroundColor = Color.RED;
            } else {
                selectionBackgroundColor = defaultBackgroundColor;
            }
        }


        return this;
    }

    @Override
    public boolean isOpaque() {
        return true;
    }

    @Override
    public void setBackground(Color bg) {
        super.setBackground(bg); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public Color getBackground() {
        return selectionBackgroundColor == null ? super.getBackground() : selectionBackgroundColor;
    }
}

Upvotes: 0

MadProgrammer
MadProgrammer

Reputation: 347204

So, after much playing around, this...

class ComboBoxRenderer extends JLabel implements ListCellRenderer {

    // Constructor
    public ComboBoxRenderer() {
        setOpaque(true);
        setHorizontalAlignment(CENTER);
        setVerticalAlignment(CENTER);
    }

    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
        Color mouseHoverHighlight = Color.LIGHT_GRAY;
        setBackground(list.getBackground());
        setText((String) value);

        // Check which item is selected
        if (isSelected) {
            // Set background color of the item your cursor is hovering over to the original background color
            setBackground(mouseHoverHighlight);
        } else {
            // Set background to specific color depending on text value
            String selectedText = getText();
            if (selectedText.equals("SelectedTextOne")) {
                setBackground(Color.GREEN);
            } else if (selectedText.equals("SelectedTextTwo")) {
                setBackground(Color.RED);
            }
        }

        // Do nothing about the text and font to be displayed
        setFont(list.getFont());

        return this;
    }
}

Seems to work. It seems setting the text after the background color was causing issues, probably triggering a repaint or something.

This is why I'd recommend using a DefaultListCellRenderer instead, it's based on a JLabel and has been optimised to reduce these kind of updates

Upvotes: 3

Related Questions