How to Change the Highlight Color of a Focused Jcombobox

How can I change the highlight color of a focused JComboBox

Update

Forget to mention. The reason you're having problems with the coloring of your combobox is because the color you are seeing is the selection color. The color is defined within the look and feel defaults, there is no way to change these colors for a single component without writing your own look and feel delegate, which I, personally wouldn't

This is an example of providing highlighting to invalid fields using JXLayer (now JLayer, but I've not had the time to convert it), while this example does use the InputVerifer API, there is no reason it has to, it's just used for part of the example. It would be quite easy to do post validation highlighting as well, the focus is on the highlighting functionality - not the method of validation ;).

This is based on the idea presented by Kirill Grouchnikov on his Pushing Pixels blog, Validation overlays using JXLayer

This is a prototype of an idea I did some time ago, the code still needs some tweaking to improve performance, but is otherwise quite functional...I'd prefer better in built support for real time validation...but that's just me ;)

Highlight

Main test class...

import com.jhlabs.image.GaussianFilter;
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import javax.swing.InputVerifier;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import org.jdesktop.jxlayer.JXLayer;
import org.jdesktop.jxlayer.plaf.AbstractLayerUI;

public class FormValidationExample {

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

public FormValidationExample() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

public class TestPane extends JPanel {

private JXLayer<JPanel> layer;

private javax.swing.JComboBox cboOne;
private javax.swing.JTextField txtOne;
private javax.swing.JTextField txtTwo;

private DefaultValidationHighlightModel validationModel;
private boolean ignoreValidationRequest;

public TestPane() {
setLayout(new BorderLayout());
JPanel content = new JPanel(new GridBagLayout());

ValidationUI ui = new ValidationUI();
validationModel = new DefaultValidationHighlightModel(ui);

layer = new JXLayer<>(content, ui);
add(layer);

cboOne = new javax.swing.JComboBox();
cboOne.setInputVerifier(new AbstractValidationInputVerifier(validationModel) {
@Override
public boolean verify(JComponent input) {
boolean valid = false;
JComboBox cb = (JComboBox) input;
String textOfOne = txtOne.getText();
String textOfTwo = txtTwo.getText();
if (cb.getSelectedIndex() == 2) {
valid = true;
} else if (cb.getSelectedIndex() == 0
&& "123".equals(textOfOne)
&& "456".equals(textOfTwo)) {
valid = true;
} else if (cb.getSelectedIndex() == 1
&& "456".equals(textOfOne)
&& "789".equals(textOfTwo)) {
valid = true;
}
return valid;
}
});
txtOne = new javax.swing.JTextField("123", 10);
txtOne.setInputVerifier(new AbstractValidationInputVerifier(validationModel) {
@Override
public boolean verify(JComponent input) {
JTextField field = (JTextField) input;
String text = field.getText();
return "123".equals(text) || "456".equals(text);
}
});
txtTwo = new javax.swing.JTextField("123", 10);
txtTwo.setInputVerifier(new AbstractValidationInputVerifier(validationModel) {
@Override
public boolean verify(JComponent input) {
JTextField field = (JTextField) input;
String text = field.getText();
return "456".equals(text) || "789".equals(text);
}
});

cboOne.setModel(new javax.swing.DefaultComboBoxModel(new String[]{"Only works with 123, 456", "Only works with 456, 789", "Works with everybody"}));

GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(4, 4, 4, 4);

content.add(cboOne, gbc);
content.add(txtOne, gbc);
content.add(txtTwo, gbc);

validateFields();

}

protected void validateFields() {
if (!ignoreValidationRequest) {
ignoreValidationRequest = true;
try {
cboOne.getInputVerifier().shouldYieldFocus(cboOne);
txtOne.getInputVerifier().shouldYieldFocus(txtOne);
txtTwo.getInputVerifier().shouldYieldFocus(txtTwo);
} finally {
ignoreValidationRequest = false;
}
}
}

public abstract class AbstractValidationInputVerifier extends InputVerifier {

private IValidationHighlightModel model;

public AbstractValidationInputVerifier(IValidationHighlightModel model) {
this.model = model;
}

public IValidationHighlightModel getModel() {
return model;
}

@Override
public boolean shouldYieldFocus(JComponent input) {
if (verify(input)) {
getModel().removeInvalidField(input);
} else {
getModel().addInvalidField(input);
}
validateFields();
return true;
}

}

}
}

JXLayer related, highlight UI layers...

public class ValidationUI extends HighlightComponentUI {

public ValidationUI() {
super(Color.RED);
}

}

public class HighlightComponentUI extends AbstractLayerUI<JPanel> {

private List<WeakReference<Component>> lstHighlights;
private Color highlightColor;

public HighlightComponentUI(Color highlight) {
highlightColor = highlight;
lstHighlights = new ArrayList<WeakReference<Component>>(25);
}

protected void cleanReferences() {
if (lstHighlights.size() > 0) {
List<WeakReference<Component>> removed = new ArrayList<WeakReference<Component>>(lstHighlights.size());
for (WeakReference<Component> wr : lstHighlights) {
Component weak = wr.get();
if (weak == null) {
removed.add(wr);
}
}
lstHighlights.removeAll(removed);
setDirty(true);
}
}

protected boolean contains(Component comp) {
boolean contains = false;
cleanReferences();
for (WeakReference<Component> wr : lstHighlights) {
Component weak = wr.get();
if (weak.equals(comp)) {
contains = true;
break;
}
}
return contains;
}

protected void clearHighlights() {
lstHighlights.clear();
setDirty(true);
}

protected void addHighlight(Component comp) {
if (comp != null) {
if (!contains(comp)) {
lstHighlights.add(new WeakReference<Component>(comp));
setDirty(true);
}
}
}

public Component[] getHighlightedComponents() {
List<Component> comps = new ArrayList<>(lstHighlights.size());
for (WeakReference<Component> wr : lstHighlights) {
Component comp = wr.get();
if (comp != null) {
comps.add(comp);
}
}
return comps.toArray(new Component[comps.size()]);
}

protected void removeHighlight(Component comp) {
cleanReferences();
WeakReference<Component> toRemove = null;
for (WeakReference<Component> wr : lstHighlights) {
Component weak = wr.get();
if (weak.equals(comp)) {
toRemove = wr;
break;
}
}
if (toRemove != null) {
lstHighlights.remove(toRemove);
setDirty(true);
}
}

public Color getHighlight() {
return highlightColor;
}

/**
* Does a recursive search of all the child components of the supplied
* parent looking for the supplied child
*
* @param parent
* @param child
* @return true if the child resides within the parent's hierarchy,
* otherwise false
*/
public boolean contains(Container parent, Component child) {

boolean contains = false;
if (child.getParent() != null) {
if (child.getParent().equals(parent)) {
contains = true;
} else {
for (Component comp : parent.getComponents()) {
if (comp instanceof Container) {
if (contains((Container) comp, child)) {
contains = true;
break;
}
}
}
}
}

return contains;
}

@Override
protected void paintLayer(Graphics2D g2, JXLayer<? extends JPanel> l) {
super.paintLayer(g2, l);
Graphics2D c = (Graphics2D) g2.create();
JComponent view = l.getView();
while (view instanceof JXLayer) {
view = (JComponent) ((JXLayer) view).getView();
}
for (WeakReference<Component> wr : lstHighlights) {

Component comp = wr.get();
if (comp != null && contains(view, comp)) {

// A cache here would be VERY useful, would need to be mainatined
// against the component instance as well as the component
// size properties...
BufferedImage img = new BufferedImage(comp.getWidth(), comp.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setComposite(AlphaComposite.Clear);
g2d.fillRect(0, 0, img.getWidth(), img.getHeight());
g2d.setComposite(AlphaComposite.SrcOver);
comp.printAll(g2d);
g2d.dispose();

BufferedImage glow = GlowEffectFactory.generateGlow(img, 8, getHighlight(), 0.75f);

Point point = comp.getLocation();
point = SwingUtilities.convertPoint(comp.getParent(), point, view);

int x = point.x - ((glow.getWidth() - comp.getWidth()) / 2);
int y = point.y - ((glow.getHeight() - comp.getHeight()) / 2);
c.drawImage(glow, x, y, l);

}
}
c.dispose();
}
}

Validation model related class (I like to use interfaces and abstract implementations to provide flexibility to the API and reduce the coupling where I can). My original prototype had the UI layer and model separated and updated via the ChangeListener support, but I combined them here for simplicity...

public class DefaultValidationHighlightModel extends AbstractValidationHighlightModel {

private HighlightComponentUI ui;

public DefaultValidationHighlightModel(HighlightComponentUI ui) {
this.ui = ui;
}

@Override
public void addInvalidField(Component comp) {
if (!ui.contains(comp)) {
ui.addHighlight(comp);
fireStateChanged();
}
}

@Override
public void removeInvalidField(Component comp) {
if (ui.contains(comp)) {
ui.removeHighlight(comp);
fireStateChanged();
}
}

@Override
public Component[] getInvalidFields() {
return ui.getHighlightedComponents();
}

}

public abstract class AbstractValidationHighlightModel implements IValidationHighlightModel {

private EventListenerList listenerList;

public EventListenerList getListenerList() {

if (listenerList == null) {

listenerList = new EventListenerList();

}

return listenerList;

}

@Override
public void addChangeListener(ChangeListener listener) {

getListenerList().add(ChangeListener.class, listener);

}

@Override
public void removeChangeListener(ChangeListener listener) {

getListenerList().remove(ChangeListener.class, listener);

}

protected ChangeListener[] getChangeListeners() {

return getListenerList().getListeners(ChangeListener.class);

}

protected void fireStateChanged() {

ChangeListener[] listeners = getChangeListeners();
if (listeners != null && listeners.length > 0) {

ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {

listener.stateChanged(evt);

}

}

}

}

public interface IValidationHighlightModel {
public void addInvalidField(Component comp);
public void removeInvalidField(Component comp);
public Component[] getInvalidFields();
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
}

public static class GlowEffectFactory {

public static BufferedImage createCompatibleImage(int width, int height) {

return createCompatibleImage(width, height, Transparency.TRANSLUCENT);

}

public static BufferedImage createCompatibleImage(Dimension size) {

return createCompatibleImage(size.width, size.height);

}

public static BufferedImage createCompatibleImage(int width, int height, int transparency) {

GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();

BufferedImage image = gc.createCompatibleImage(width, height, transparency);
image.coerceData(true);
return image;

}

public static BufferedImage applyMask(BufferedImage sourceImage, BufferedImage maskImage, int method) {

BufferedImage maskedImage = null;
if (sourceImage != null) {

int width = maskImage.getWidth(null);
int height = maskImage.getHeight(null);

maskedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D mg = maskedImage.createGraphics();

int x = (width - sourceImage.getWidth(null)) / 2;
int y = (height - sourceImage.getHeight(null)) / 2;

mg.drawImage(sourceImage, x, y, null);
mg.setComposite(AlphaComposite.getInstance(method));

mg.drawImage(maskImage, 0, 0, null);

mg.dispose();

}

return maskedImage;

}

public static BufferedImage generateBlur(BufferedImage imgSource, int size, Color color, float alpha) {

GaussianFilter filter = new GaussianFilter(size);

int imgWidth = imgSource.getWidth();
int imgHeight = imgSource.getHeight();

BufferedImage imgBlur = createCompatibleImage(imgWidth, imgHeight);
Graphics2D g2 = imgBlur.createGraphics();

g2.drawImage(imgSource, 0, 0, null);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, alpha));
g2.setColor(color);

g2.fillRect(0, 0, imgSource.getWidth(), imgSource.getHeight());
g2.dispose();

imgBlur = filter.filter(imgBlur, null);

return imgBlur;

}

public static BufferedImage generateBlur(BufferedImage imgSource, int size) {

GaussianFilter filter = new GaussianFilter(size);

int imgWidth = imgSource.getWidth();
int imgHeight = imgSource.getHeight();

BufferedImage imgBlur = createCompatibleImage(imgWidth, imgHeight);
Graphics2D g2 = imgBlur.createGraphics();

g2.drawImage(imgSource, 0, 0, null);
g2.dispose();

imgBlur = filter.filter(imgBlur, null);

return imgBlur;

}

public static BufferedImage generateGlow(BufferedImage imgSource, int size, Color color, float alpha) {

int imgWidth = imgSource.getWidth() + (size * 2);
int imgHeight = imgSource.getHeight() + (size * 2);

BufferedImage imgMask = createCompatibleImage(imgWidth, imgHeight);
Graphics2D g2 = imgMask.createGraphics();

int x = Math.round((imgWidth - imgSource.getWidth()) / 2f);
int y = Math.round((imgHeight - imgSource.getHeight()) / 2f);
g2.drawImage(imgSource, x, y, null);
g2.dispose();

// ---- Blur here ---
BufferedImage imgGlow = generateBlur(imgMask, size, color, alpha);

// ---- Blur here ----
imgGlow = applyMask(imgGlow, imgMask, AlphaComposite.DST_OUT);

return imgGlow;

}

}

Caveats

This requires JXLayer (I was using version 3) (which no longer seem to be available on the net...) and SwingX (I was using version 1.6.4)

I've put all the source code of JXLayer (version 3) and Piet's examples into a single zip and I would suggest, if you are interested, you grab a copy and store it some where safe.

You will also need JHLabs filters

Colored jcombobox with colored items and focus

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

custom JComboBoxRenderer change background color depending on text selection

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

Highlight text in JComboBox then erase it if user input something

You need to add the logic to the editor of the combo box which happens to be a text field.

The basic code would be something like:

    ComboBoxEditor editor = comboBox.getEditor();
JTextField textField = (JTextField)editor.getEditorComponent();
textField.addFocusListener( new FocusListener()
{
public void focusGained(final FocusEvent e)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
JTextField textField = (JTextField)e.getSource();
textField.selectAll();
}
});
}

public void focusLost(FocusEvent e) {}
});

Delete highlighting in JComboBox

To make it easier for people with a similar problem, here is the code for the renderer I wrote:

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

class ComboBoxRenderer extends JLabel implements ListCellRenderer
{
private boolean colorSet;
private Color selectionBackgroundColor;

public ComboBoxRenderer()
{
setOpaque(true);
setHorizontalAlignment(LEFT);
setVerticalAlignment(CENTER);
colorSet = false;
selectionBackgroundColor = Color.red; // Have to set a color, else a compiler error will occur
}

public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)
{
// Check if color is set (only runs the first time)
if(!colorSet)
{
// Set the list' background color to original selection background of the list
selectionBackgroundColor = list.getSelectionBackground();
// Do this only one time since the color will change later
colorSet = true;
}

// Set the list' background color to white (white will show once selection is made)
list.setSelectionBackground(Color.white);

// Check which item is selected
if(isSelected)
{
// Set background color of the item your cursor is hovering over to the original background color
setBackground(selectionBackgroundColor);
}
else
{
// Set background color of all other items to white
setBackground(Color.white);
}

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

return this;
}
}

Edit: Previous code didn't seem to work as properly, code updated and should work all fine now

Java JCombobox coloring of selected item not updated until focus lost

A JComboBox is displayed using a single renderer for all cells. You can change the color in your ListCellRenderer, as shown in the CustomComboBoxDemo.

Addendum: Related examples may be found in this Q&A.

JComboBox setBackground() without changing the color of the arrow

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

P.S. The same approach works also for font and foregorund.



Related Topics



Leave a reply



Submit