如何根据字段的当前编辑内容更改 JSpinner 的背景颜色?

How to change the background color of a JSpinner dependent on the current edited content of the field?

我有一个带有 JSpinner 使用 SpinnerNumberModel 使用 double 值的 GUI。

一改JSpinnerEditor的内容,我就想把背景改成黄色(以表明当前显示的值不是那个"saved" 在 JSpinner 分别是它的 Model.

如果该内容无效(例如,超出我的 SpinnerNumberModel 指定的允许范围或文本 "abc"),背景应更改为红色。

我已经尝试用 FocusListener 实现我想要的效果,但还没有成功,我也不确定它是否可以工作,因为我需要检查聚焦和散焦之间的内容.

我检查了 Swing 组件的所有 Listeners 教程,但找不到适合该工作的正确教程。 (here I informed myself)

我是 Listeners 概念的新手,非常感谢任何帮助我更接近解决问题的帮助,同时也有助于总体理解 Listeners 以及如何在这种情况下更好地使用它们!

我真正基本的代码示例,上面提到的使用焦点侦听器的糟糕尝试:

public class test implements FocusListener{

JFrame frame;

SpinnerNumberModel model;
JSpinner spinner;
JComponent comp;
JFormattedTextField field;

public test() {
    JFrame frame = new JFrame("frame");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));

    model = new SpinnerNumberModel(0., 0., 100., 0.1);
    spinner = new JSpinner(model);
    comp = spinner.getEditor();
    field = (JFormattedTextField) comp.getComponent(0);
    field.addFocusListener(this);

    frame.getContentPane().add(spinner);
    frame.getContentPane().add(new JButton("defocus spinner")); //to have something to defocus when testing :)
    frame.pack();
    frame.setVisible(true);
}

@Override
public void focusGained(FocusEvent e) {
    // TODO Auto-generated method stub
    //when the values of the field and the spinner don't match, the field should get yellow
    if(!field.getValue().equals(spinner.getModel().getValue())) {
        field.setBackground(Color.YELLOW);
    }
}

@Override
public void focusLost(FocusEvent e) {
    // TODO Auto-generated method stub
    //if they match again, reset to white
            if(!field.getValue().equals(spinner.getModel().getValue())) {
                field.setBackground(Color.RED);
            }
}
}

JSpinner 使用文本字段作为微调器的编辑器

因此,您可以将 DocumentListener 添加到用作编辑器的文本字段的 Document

类似于:

JTextField textField = ((JSpinner.DefaultEditor)spinner.getEditor()).getTextField());
textField.getDocument.addDocumentListener(...);

然后当文本为 added/removed 时,将生成一个 DocumentEvent,您可以进行错误检查。阅读有关 Listener For Changes on a Document 的 Swing 教程部分,了解更多信息和工作示例。

你可以使用 CaretListener ,这是一个开始:

import java.awt.Color;
import java.awt.Component;
import javax.swing.BoxLayout;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;

public class SpinerTest{

    JSpinner spinner;

    public SpinerTest() {
        JFrame frame = new JFrame("frame");
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(300, 200);
        frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
        SpinnerNumberModel model = new SpinnerNumberModel(0., 0., 100., 0.1);
        spinner = new JSpinner(model);
        setCaretListener();
        frame.getContentPane().add(spinner);
        frame.pack();
        frame.setVisible(true);
    }

    private void setCaretListener() {

        for(Component c : spinner.getEditor().getComponents()) {

            JFormattedTextField field =(JFormattedTextField) c;

            field.addCaretListener(new CaretListener(){
                @Override
                public void caretUpdate(CaretEvent ce) {

                    if (field.isEditValid()) {
                        //add aditional test as needed
                        System.out.println("valid Edit Entered " + field.getText());
                        field.setBackground(Color.WHITE);
                    }
                    else                {
                        System.out.println("Invalid Edit Entered" + field.getText());
                        field.setBackground(Color.PINK);
                    }
                }
            });

        }
    }

    public static void main(String[] args) {

        new SpinerTest();
    }
}

我能够通过 KeyListenerDocumentListenerFocusListener 的组合完成任务。解决方案可能不是最简单的,但最后我编写了某事。这样可行。附加文件中的评论应该解释我是如何处理这个问题的。

我用一个不是我写的 CommaReplacingNumericDocumentFilter expands DocumentFilter class 扩展了原始任务,我从我的教授那里得到了代码,并根据我的需要对其进行了编辑。现在只有 digits, minuse, E被接受为 JSpinner 中的条目。 逗号 替换 也用

代码:

import java.awt.*;
import java.awt.event.*;
import java.util.Locale;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;


public class test implements DocumentListener, ChangeListener, KeyListener{
    boolean keyPressed;
    JFrame frame;   
    SpinnerNumberModel model;
    JSpinner spinner;
    JComponent comp;
    JFormattedTextField field;

public test() {
    JFrame frame = new JFrame("frame");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));

    model = new SpinnerNumberModel(0., 0., 100000., .1);
    spinner = new JSpinner(model);

    //disable grouping for spinner
    JSpinner.NumberEditor editor = new JSpinner.NumberEditor(spinner);
    editor.getFormat().setGroupingUsed(false);
    spinner.setEditor(editor);

    comp = spinner.getEditor();
    field = (JFormattedTextField) comp.getComponent(0);
    field.getDocument().addDocumentListener(this);
    field.addKeyListener(this);
    spinner.addChangeListener(this);

    frame.getContentPane().add(spinner);
    frame.pack();
    frame.setVisible(true);
}

@Override
public void insertUpdate(DocumentEvent e) {
    DocumentEventHandler(e);
}

@Override
public void removeUpdate(DocumentEvent e) {
    DocumentEventHandler(e);
}

@Override
public void changedUpdate(DocumentEvent e) {
    DocumentEventHandler(e);
}

public static boolean isNumeric(String str)  
{  
  try  
  {  
    double d = Double.parseDouble(str);  
  }  
  catch(NumberFormatException nfe)  
  {  
    return false;  
  }  
  return true;  
}

public static void main(String[] args) {
    //to get the right format for double precision numbers
    Locale.setDefault(Locale.US);
    test test = new test();
}

@Override
public void stateChanged(ChangeEvent e) {
    System.out.println("valuechanged: " + spinner.getValue().toString());
    if(keyPressed) {
        field.setBackground(Color.WHITE);
    }
    keyPressed = false;
}

public void DocumentEventHandler(DocumentEvent e) {
    //as soon as update is inserted, set background to yellow
    if (keyPressed) {
        field.setBackground(Color.YELLOW);  

        //check if input is numeric and in bounds
        String text = field.getText();
        if (isNumeric(text)) {
            double value = Double.parseDouble(text);
            if (value < (Double)model.getMinimum() || value > (Double)model.getMaximum()) {
                field.setBackground(Color.RED);
            }
        }
        else { //set background to red
            field.setBackground(Color.RED);
        } 
    }

    keyPressed = false;

    //System.out.println(e.toString());
    //System.out.println("Text: " + field.getText());
}

@Override
public void keyTyped(KeyEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void keyReleased(KeyEvent e) {
    // TODO Auto-generated method stub

}

 /** If not done yet, replaces the DocumentFilter with one replacing commas by decimal points.
    *  This can't be done at the very beginning because the DocumentFilter would be changed to a
    *  javax.swing.text.DefaultFormatter$DefaultDocumentFilter when setting up the JSpinner GUI. */
   public void keyPressed(KeyEvent e) {
       PlainDocument document = (PlainDocument)(field.getDocument());
       if(!(document.getDocumentFilter() instanceof CommaReplacingNumericDocumentFilter))
           document.setDocumentFilter(new CommaReplacingNumericDocumentFilter());
       /*Tell the other handlers that a key has been pressed and the change in the document does
        * not come from using the JSpinner buttons or the MouseWheel.
        */
       keyPressed = true;
   }



}

/** A javax.swing.text.DocumentFilter that replaces commas to decimal points
 *  and ignores non-numeric characters except 'e' and 'E'. This is called before
 *  modi */
class CommaReplacingNumericDocumentFilter extends DocumentFilter {
    @Override
    public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr)
            throws BadLocationException {
    text = filter(text);
    if (text.length() > 0)
        super.insertString(fb, offset, text, attr);
}

@Override
public void replace(FilterBypass fb, int offset, int length, String text,
    AttributeSet attrs) throws BadLocationException {
    text = filter(text);
    if (text.length() > 0)
        super.replace(fb, offset, length, text, attrs);
}

String filter(String text) {
    return text.replace(',', '.').replaceAll("[^0-9eE.-]","");
}
}