首先让我解释一下我期望实现的目标。我在Swing中创建了一个数据输入表单,由多个JComboBox和&组成。 JTextField的。验证例程迭代这些组件并确定为每个控件指定的值是否有效' (验证的细节与本例的目的无关)。
当例程识别出组件包含无效值时,我想要更改 该字段的背景颜色,以及该字段的前景/文本颜色 - 使用户清楚该字段存在问题。
如果字段被视为“有效”,我想将控件的背景设置为白色 - 前景/文本设置为黑色。
到目前为止,所有这些都非常简单,并且可以在下面附带的演示代码中实现。
当组合框包含有效值并且被聚焦时 - 组合中编辑器的背景设置为蓝色,我非常满意。
然而,我想要实现的是当该组合框包含无效值时,更改用于突出显示焦点组合框的颜色。尽管将组合框的背景颜色更改为粉红色,但如果控件是聚焦的,它仍然使用蓝色来表示它是聚焦的。
关注的无效字段示例: http://postimg.org/image/ne9xgjch3/
虽然我很欣赏这是完全正常的行为,但我想做的是改变用于突出显示其中一个无效'字段为深色的颜色,非聚焦&无效控制将具有 - 这样用户仍然可以看到哪个控件是聚焦的,并且它仍然是粉红色的。我很欣赏这可能看起来很小,但我的最终用户坚持认为整个领域在聚焦时仍然是粉红色(或者更确切地说,是不同的粉红色)。这就是我的eutopia,一个专注且无效的' 字段,看起来像:
http://postimg.org/image/9793bqcfj/
我尝试过扩展DefaultListCellRenderer& BasicComboBoxEditor类,并将组合框分别设置为渲染器和编辑器。我的印象是编辑器将成为我需要集中注意力的地方,因此在类的getEditorComponent方法中,我将返回一个具有适当背景和前景的标签 - 但是从该方法中,我无法知道控件是否具有焦点,因此无法确定如何格式化返回的标签。另外,一旦我开始针对组合框设置编辑器,我似乎完全失去了控制焦点的能力 - 尽管这可能是我对如何实现编辑器缺乏了解。
我也一直在阅读有关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
}
答案 0 :(得分:4)
<强>更新强>
忘记提及。您遇到组合框着色问题的原因是因为您看到的颜色是选择颜色。颜色是在外观默认情况下定义的,没有办法在不编写自己的外观和感觉委托的情况下为单个组件更改这些颜色,我个人不会
这是使用JXLayer
(现在为JLayer
提供高亮显示无效字段的示例,但我没有时间对其进行转换),而此示例使用{{1} API,没有理由,它只是用于部分示例。进行验证后突出显示也非常容易,重点是突出显示功能 - 而不是验证方法;)。
这是基于Kirill Grouchnikov在他的Pushing Pixels博客上提出的想法,Validation overlays using JXLayer
这是我前段时间做过的一个想法的原型,代码仍然需要一些调整以提高性能,但其他方面功能相当......我更喜欢更好的内置支持实时验证......但是那只是我;)
主要考试班......
InputVerifer
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;
}
}
}
}
相关,突出显示UI图层...
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'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();
}
}
和interfaces
实现来为API提供灵活性并减少我可以的耦合。我的原始原型通过abstract
支持分离和更新了UI层和模型,但为了简单起见,我将它们组合在一起......
ChangeListener
<强>注意事项强>
这需要JXLayer(我使用的是版本3)(网上似乎不再可用)和SwingX(我使用的是1.6.4版本)
我已将JXLayer(版本3)和Piet示例的所有源代码放入单个zip中,我建议,如果您有兴趣,可以抓取副本并将其存储在安全的地方。 / p>
您还需要JHLabs filters