How to change the highlight color of a focused JComboBox

Let me first explain what I want to achieve. I am creating a Swing data entry form consisting of several JComboBoxes and JTextFields. The verification procedure iterates over these components and determines whether the values ​​specified for each control are "valid" (the verification details are not relevant for the purposes of this example).

When the subroutine indicates that the component contains an invalid value, I want to change the background color of this field, as well as the foreground / text color of this field - so that the user makes it clear that there is a problem with this field.

If the field is considered “valid”, I want the background of the control to be white and the foreground / text black.

So far, everything is pretty straightforward, and all this was available in the attached demo code below.

When the Combo Box contains a valid value and is focused, the background of the editor inside the combo is set to a bluish color, which I am completely satisfied with.

However, I am trying to change the color that is used to highlight a focused combo box when this combo box contains an invalid value. Although the background color of the combo box was pink when the control is focused, it still uses blue to indicate that it is focused.

An example of an invalid field that is focused: http://postimg.org/image/ne9xgjch3/

, , , , - , "" , - , , , . , , , (, ). , , "", , :

http://postimg.org/image/9793bqcfj/

DefaultListCellRenderer BasicComboBoxEditor . , , , getEditorComponent - , , , . , combobox, , , , , , , .

BasicComboBoxUI, , , .

, - , , , . , -, netbeans, .

package com.test;

import java.awt.*;

public class TestForm extends javax.swing.JFrame {

    public TestForm()
    {
        initComponents();
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        cboOne = new javax.swing.JComboBox();
        txtOne = new javax.swing.JTextField();
        txtTwo = new javax.swing.JTextField();
        btnValidate = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        cboOne.setBackground(new java.awt.Color(255, 255, 255));
        cboOne.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Valid Value", "Invalid Value", "Another Invalid Value" }));

        txtOne.setText("123");

        txtTwo.setText("123");

        btnValidate.setText("Validate");
        btnValidate.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnValidateActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(cboOne, 0, 376, Short.MAX_VALUE)
                    .addComponent(txtOne)
                    .addComponent(txtTwo)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addGap(0, 0, Short.MAX_VALUE)
                        .addComponent(btnValidate)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(cboOne, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(txtOne, javax.swing.GroupLayout.PREFERRED_SIZE, 51, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(txtTwo, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(btnValidate)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void btnValidateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnValidateActionPerformed

        //Check if the selection in the ComboBox is valid...
        if (((String)cboOne.getSelectedItem()).equals("Valid Value"))
        {
            //Selected Value is Valid.
            //We want the combo box to appear with a white background
            //and black text.
            cboOne.setBackground(Color.white);
            cboOne.setForeground(Color.black);
        }
        else
        {
            //The value specified is invalid.
            //We want to highlight the field in pink to identify an issue,
            //and change the color of the text to red too:
            cboOne.setBackground(Color.pink);
            cboOne.setForeground(Color.red);
        }

        //Check if the value entered into the first Text Field is valid...
        if (txtOne.getText().equals("123"))
        {
            //Selected Value is Valid.
            //We want the text box to appear with a white background
            //and black text.            
            txtOne.setBackground(Color.white);
            txtOne.setForeground(Color.black);
        }
        else
        {
            //Selected Value is invalid.
            //We want the text box to appear with a pink background
            //and red text. 
            txtOne.setBackground(Color.pink);
            txtOne.setForeground(Color.red);
        }

        //Check if the value entered into the second Text Field is valid...
        if (txtTwo.getText().equals("123"))
        {
            //Selected Value is Valid.
            //We want the text box to appear with a white background
            //and black text. 
            txtTwo.setBackground(Color.white);
            txtTwo.setForeground(Color.black);
        }
        else
        {
            //Selected Value is invalid.
            //We want the text box to appear with a pink background
            //and red text. 
            txtTwo.setBackground(Color.pink);
            txtTwo.setForeground(Color.red);
        }

    }//GEN-LAST:event_btnValidateActionPerformed

    public static void main(String args[]) {


        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new TestForm().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btnValidate;
    private javax.swing.JComboBox cboOne;
    private javax.swing.JComboBox jComboBox1;
    private javax.swing.JComboBox jComboBox2;
    private javax.swing.JTextField txtOne;
    private javax.swing.JTextField txtTwo;
    // End of variables declaration//GEN-END:variables
}
+1
1

. , combobox, , , , . , , ,

JXLayer ( JLayer, ), API InputVerifer, , , . , , ;).

Pushing Pixels, JXLayer

, , , ... ... ;)

Highlight

...

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, ...

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

, ( interfaces abstract API , ). , ChangeListener, ...

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;

    }

}

JXLayer ( 3) ( ...) SwingX ( 1.6.4)

JXLayer ( 3) Piet zip, , , , .

JHLabs

+4

Source: https://habr.com/ru/post/1669131/


All Articles