2

I have a panel containing textfields (red, green, blue) and sliders (red, green, blue). Users are supposed to be able to utilize whichever they prefer, and the corresponding component should update. (E.g., entering 100 into the red textfield should move the red slider to 100).

Since I want the sliders to update as text is being inserted or removed, I created a simple DocumentListener. However, I get the error message "java.lang.IllegalStateException: Attempt to mutate in notification." My program gets confused as to whether it should updated the slider or textfield first. Is there a way to fix this problem? I will post my two classes below, and a photo of the GUI will also be attached for quick reference.enter image description here

public class ColorChooser extends javax.swing.JPanel implements ChangeListener {
    private Vector listeners;

    public ColorChooser() {
        initComponents();
        listeners = new Vector();
        sldRed.addChangeListener(this);
        sldGreen.addChangeListener(this);
        sldBlue.addChangeListener(this);
        txtRed.setText("0");
        txtGreen.setText("0");
        txtBlue.setText("0");
        Document docRed = txtRed.getDocument();
        docRed.addDocumentListener(new MyDocumentListener(txtRed, sldRed));
        Document docGreen = txtGreen.getDocument();
        docGreen.addDocumentListener(new MyDocumentListener(txtGreen, sldGreen));
        Document docBlue = txtBlue.getDocument();
        docBlue.addDocumentListener(new MyDocumentListener(txtBlue, sldBlue));
}

    // Variables declaration - do not modify                     
    private javax.swing.JLabel labelBlue;
    private javax.swing.JLabel labelGreen;
    private javax.swing.JLabel labelRed;
    private javax.swing.JSlider sldBlue;
    private javax.swing.JSlider sldGreen;
    private javax.swing.JSlider sldRed;
    private jcolorchooser.JColorIntegerField txtBlue;
    private jcolorchooser.JColorIntegerField txtGreen;
    private jcolorchooser.JColorIntegerField txtRed;
    // End of variables declaration                   

    @Override
    public void stateChanged(ChangeEvent ce) {
        if (ce.getSource() == sldRed) {
            txtRed.setText(Integer.toString(sldRed.getValue()));
        }
        if (ce.getSource() == sldGreen) {
            txtGreen.setText(Integer.toString(sldGreen.getValue()));
        }
        if (ce.getSource() == sldBlue) {
            txtBlue.setText(Integer.toString(sldBlue.getValue()));
        }
        int r = sldRed.getValue();
        int g = sldGreen.getValue();
        int b = sldBlue.getValue();
        Color color = new Color(r,g,b);
        fireColorEvent(new ColorEvent(this,color));
    }
    private void fireColorEvent(ColorEvent colorEvent){
        Vector v;
        synchronized(this){
            v = (Vector)listeners.clone();
        }
        int size = v.size();
        for(int i=0; i<size; i++){
            ColorListener colorListener = (ColorListener)v.elementAt(i);
            colorListener.changeColor(colorEvent);
        }
    }
    public void addColorListener(ColorListener colorListener){
        listeners.addElement(colorListener);
    }
    public void removeColorListener(ColorListener colorListener){
        listeners.removeElement(colorListener);
   }

}

//DocumentListener Class
public class MyDocumentListener implements DocumentListener {
    private JColorIntegerField jColorIntegerField;
    private JSlider jColorSlider;

    public MyDocumentListener(JColorIntegerField jColorIntegerField,
        JSlider jColorSlider) {
        this.jColorIntegerField = jColorIntegerField;
        this.jColorSlider = jColorSlider;
    }

    public void insertUpdate(DocumentEvent de) {
        if (jColorIntegerField.getText().equals("")) {
            jColorSlider.setValue(0);
        }
        else {
            jColorSlider.setValue(Integer.parseInt(
                jColorIntegerField.getText()));
        }
    }

    @Override
    public void removeUpdate(DocumentEvent de) {
        if (jColorIntegerField.getText().equals("")) {
            jColorSlider.setValue(0);
        }
        else {
            jColorSlider.setValue(Integer.parseInt(
                jColorIntegerField.getText()));
        }
    }

    @Override
    public void changedUpdate(DocumentEvent de) {

    }
}
Drag and Drop
  • 2,672
  • 3
  • 25
  • 37

1 Answers1

1

This may come a little late, but I was working on a Java project this week and I had the same issue as the one you described above.

While searching for a solution, I stumbled upon your question. As it doesn't have an answer yet, I thought it could be helpful to come back here and describe how I solved the problem (maybe it will help you or someone else in the future).

So, it seems that the Swing API helps us here by offering a very nice method named getValueIsAdjusting. This method is part of the JSlider class.

How does this solve the problem?

We modify the stateChanged method from the ChangeListener implementation:

@Override
public void stateChanged(ChangeEvent ce) {
    JSlider source = (JSlider)ce.getSource();

    if (source.getValueIsAdjusting()) {
        if (source == sldRed)
            txtRed.setText(Integer.toString(sldRed.getValue()));
        else if (source == sldGreen)
            txtGreen.setText(Integer.toString(sldGreen.getValue()));
        else if (source == sldBlue)
            txtBlue.setText(Integer.toString(sldBlue.getValue()));
    }

    int r = sldRed.getValue();
    int g = sldGreen.getValue();
    int b = sldBlue.getValue();
    Color color = new Color(r, g, b);

    fireColorEvent(new ColorEvent(this, color));
}

The if (source.getValueIsAdjusting()) instruction makes the setText's happen only if you are still "adjusting" the slider (that means you are still dragging it with the mouse).

Writing a value into the JTextField will trigger the stateChanged method from the sliders, but it will NOT call the setText's, because the slider value is not "adjusting".

Thus, you get rid of the exception.

Valy
  • 573
  • 2
  • 12