简体   繁体   中英

JComboBox setBackground() without changing the color of the arrow

I have been trying to figure this out for 6 hours now. I finally got the colors to change as desired, but I just want to keep the default color for the arrow of the combobox.

Here is the code...

Note: Edited code to compile without background shown in pictures...

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);
                            if (charName.equals("SelectedTextOne")){
                                //charCombo.getEditor().getEditorComponent().setBackground(Color.GREEN);
                                charCombo.setBackground(Color.GREEN);
                            } else if (charName.equals("SelectedTextTwo")){
                                charCombo.setBackground(Color.RED);
                            } else {
                                charCombo.setBackground(dlcr.getBackground());
                            }
                        }
                    }
                }
        );
    }

    //**************************************************************************************
    // 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();
    }
}

Note: I am using a custom renderer. Here is the code for that...

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);
            } else {
                setBackground(this.dlcr.getBackground());
            }
        }
        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;
    }
}

Steps to reproduce error:

在此输入图像描述

在此输入图像描述

Expected Result

在此输入图像描述

Your approach is correct, but BasicComboBoxUI put a spoke in your wheel. Fortunately I have a trick for you, which helps to avoid this problem.

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;

import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.swing.plaf.basic.BasicComboBoxRenderer;

@SuppressWarnings("unchecked")
public class ComboBgTest {

    private static final String[] VALUES = {"One", "Two", "Three"};
    @SuppressWarnings("serial")
    public static void main(String[] args) {
        JComboBox<String> cb = new JComboBox<>(VALUES);
        cb.setSelectedItem(null);
        cb.setRenderer(new BasicComboBoxRenderer() {
            boolean ignoreBG = true;
            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
                    boolean cellHasFocus) {
                ignoreBG = false;
                Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (index == -1) { // check whether we are render the item shown at top
                    if (VALUES[0].equals(value)) {
                        c.setBackground(Color.RED);
                    } else if (VALUES[1].equals(value)) {
                        setBackground(Color.GREEN);
                    } else if (VALUES[2].equals(value)) {
                        setBackground(Color.BLUE);
                    }
                }
                ignoreBG = true;
                return this;
            }

            @Override
            public void setBackground(Color bg) {
                // ignore all BG which is set from outside.
                if (!ignoreBG) {
                    super.setBackground(bg);
                }
            }
        });
        JFrame frm = new JFrame("Combo test");
        frm.add(cb);
        frm.pack();
        frm.setLocationRelativeTo(null);
        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.setVisible(true);
    }
}

PS The same approach works also for font and foregorund.

[rant] You really have to learn to reduce your example code down to the minimum requirement to demonstrate the problem, it takes a lot of effort to refactor the code every time :P [/rant]

What's happening is, the JComboBox is changing the background color of the selected item AFTER it's been selected (and rendered) from the list. One possible solution is to simply circumvent this by having a "override" color which is always returned, regardless of what is set, for example...

背景改变了 背景改变了

import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.HeadlessException;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestGui extends JFrame {

    private final String[] guiCharSel = {"---  Select Character ---", "SelectedTextOne", "SelectedTextTwo", "SelectedTextThree", "SelectedTextFour"};

    public static void main(String[] args) {
        new TestGui();
    }

    public TestGui() throws HeadlessException {
        try {
            UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
            ex.printStackTrace();
        }
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JComboBox box = new JComboBox(guiCharSel);
                box.setRenderer(new ComboBoxRenderer());

                JTextField field = new JTextField(10);

                JFrame frame = new JFrame();
                frame.setLayout(new GridBagLayout());
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridwidth = GridBagConstraints.REMAINDER;
                frame.add(box, gbc);
                frame.add(field, gbc);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    class ComboBoxRenderer extends DefaultListCellRenderer {

        private Color overrideBackground;
        private Color mouseHoverHighlight = Color.LIGHT_GRAY;

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

        @Override
        public void setOpaque(boolean isOpaque) {
            System.out.println("setOpaque " + isOpaque);
            //NOOP
        }

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

        @Override
        public void setBackground(Color bg) {
            System.out.println(">> Background = " + bg);
            super.setBackground(bg); //To change body of generated methods, choose Tools | Templates.
        }

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

        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)
            overrideBackground = null;
            setText((String) value);

            // Check which item is selected
            System.out.println(value + "; isSelected = " + isSelected + "; cellHasFocus = " + cellHasFocus);
            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")) {
                    System.out.println(">> Green");
                    overrideBackground = Color.GREEN;
                } else if (selectedTextInDropdownList.equals("SelectedTextTwo")) {
                    System.out.println(">> Red");
                    overrideBackground = Color.RED;
                } else {
                    overrideBackground = null;
                }
            }

            return this;
        }
    }
}

This will only show up AFTER the combobox loses focus, as the item is been rendered as "selected". You could muck around with change the selection background as well, but I couldn't find any decent combination of states where it was simple to do

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