如何更改焦点JComboBox的高亮颜色

让我先解释一下我期待的成就。 我在Swing中创build了一个数据input表单,由许多JComboBoxes和JTextFields组成。 validation例程迭代这些组件,并确定为每个控件指定的值是否为“有效”(validation的细节与本示例的目的无关)。

当例程识别出一个组件包含一个无效值时,我想要改变该字段的背景颜色,以及该字段的前景/文本颜色 – 以便向用户说明该字段存在问题。

如果一个字段被认为是“有效的”,我想把控件的背景设置为白色,而前景/文本是黑色的。

所有相当直截了当的,并在所附的演示代码下面所有可实现的。

当一个combobox包含一个有效的值,并被重点 – 组合中的编辑器的背景设置为蓝色,我很满意。

但是,我试图实现的是当该combobox包含无效值时更改用于突出显示一个焦点combobox的颜色。 尽pipe已将combobox的背景颜色更改为粉色,但如果控件处于聚焦状态,仍会使用蓝色表示聚焦。

重点关注的无效字段示例: http : //postimg.org/image/ne9xgjch3/

虽然我明白这是完全正常的行为,但我想要做的是将用于突出显示“无效”字段之一的颜色更改为颜色较深的阴影,而非重点和无效的控件将会具有这种颜色 – 这样用户仍然可以看到哪个控件是关注的,而且它仍然是粉红色的。 我明白这可能看起来很小巧,但是我的最终用户坚持认为整个领域在聚焦的时候仍然是粉红色的(或者更确切地说,是一种不同的粉色)。 这就是我的eutopia,一个专注和“无效”的领域,看起来像:

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

我已经试过扩展DefaultListCellRenderer&BasicComboBoxEditor类,并分别设置combobox作为渲染器和编辑器。 我的印象是,编辑器会成为我需要关注的地方,所以在类的getEditorComponent方法中,我会返回一个带有适当背景和前景的标签 – 但是在这个方法中,我没有办法知道控件是否有焦点,所以无法确定如何格式化返回的标签。 此外,一旦我开始对combobox设置编辑器,我似乎失去了将控件集中在一起的能力 – 尽pipe这可能是我对如何实现编辑器缺乏了解。

我一直在阅读有关BasicComboBoxUI,但我遇到的东西已经脱颖而出,作为解决scheme。

请别人指点一下正确的方向,我已经花了几天的时间来修补这个问题,而且这真的开始困扰我了。 请原谅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 } 

更新

忘了提。 你的组合颜色出现问题的原因是你所看到的颜色是select颜色。 颜色是在外观和感觉的默认值内定义的,没有办法改变这些颜色的单个组件,而不写自己的外观和感觉代表,我个人不会

这是一个使用JXLayer (现在是JLayer ,但是我没有时间去转换它)提供高亮显示给无效字段的例子,虽然这个例子使用了InputVerifer API,但没有理由,它只是用于示例的一部分。 后期validation突出显示也是相当容易的,重点是突出显示function – 而不是validation方法)。

这是基于Kirill Grouchnikov在其推送像素博客上提出的想法, 使用JXLayer的validation覆盖

这是我前一段时间的想法的原型,代码仍然需要一些调整,以提高性能,但是否则相当function…我更喜欢更好的build立支持实时validation…但这只是我;)

突出

主要testing类…

 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相关,突出显示UI层…

 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模型相关类(我喜欢使用interfacesabstract实现,以提供API的灵活性,并减less耦合,我可以)。 我原来的原型有UI层和模型通过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)(networking上不再可用)和SwingX (我正在使用版本1.6.4)

我已经把JXLayer(版本3)和Piet的例子的所有源代码放到一个zip文件中 ,我build议,如果你有兴趣,你可以抓取一个副本,并把它存储在一个安全的地方。

您还需要JHLabsfilter