user3773129
user3773129

Reputation:

Setting event listener for two JTextArea's

I am developing an image processing software (just for fun) and one of the features it has is image resizing option. Basically window pops up, with two JTextArea components to get desired image width and height for resizing. There is also JCheckBox for keeping Aspect ratio if user desires it. The problem is. When check box is selected and user supposedly inputs either width or height first. I want the other text area to update itself accordingly every time a change is made so it would keep AR. I have developed some code that deals with this, but it does not provide what I really want due to lack of understanding what listener to what component should I really assign.

Code:

String height, width;
  if (checkBoxImage.isSelected()){
      // aspect ratio = width / height
      width = widthArea.getText();
      height = heightArea.getText();
      double aspectRatio = (double) images.get(tabbedPane.getSelectedIndex()).getWidth() / images.get(tabbedPane.getSelectedIndex()).getHeight();
      /**
       * to do, update width, height area
       * to the closest user input
       */
      if(heightArea.getText().length() != 0 && heightArea.getText().length() <= 5
              && heightArea.getText().charAt(0) != '0'){
          //parsing string to integer
          try{
              int heightNum = Integer.parseInt(height);
              int widthNum = (int) Math.round(aspectRatio * heightNum);
              widthArea.setText(String.valueOf(widthNum) ); 
              widthArea.updateUI();
              frameimgSize.repaint();
          }
          catch(NumberFormatException e1){JOptionPane.showMessageDialog(error,e1.getMessage(),"Error", JOptionPane.ERROR_MESSAGE);} 
      }
      //width has been entered first
      else if(widthArea.getText().length() != 0 && widthArea.getText().length() <= 5 &&
              widthArea.getText().charAt(0) != '0'){
          try{
              int widthNum = Integer.parseInt(width);
              int heightNum = (int) Math.round(aspectRatio * widthNum);
              heightArea.setText(String.valueOf(heightNum) );
              heightArea.updateUI();
              frameimgSize.repaint();
          }
          catch(NumberFormatException e1){JOptionPane.showMessageDialog(error,e1.getMessage(),"Error", JOptionPane.ERROR_MESSAGE);} 
      }
  }

Upvotes: 0

Views: 90

Answers (2)

Matt Coubrough
Matt Coubrough

Reputation: 3829

Is it ever valid to have non-numeric values in your width and height fields?

If not, then use JSpinners or JFormattedTextFields instead of JTextFields. If so, (say for example you allow a "units" to be entered as well as width and height) you should attach a DocumentListener to your JTextFields to monitor changes to the content of the underlying text documents. Here's an example:

widthField.getDocument().addDocumentListener(new DocumentListener() {
      public void changedUpdate(DocumentEvent e) {
          update();
      }
      public void removeUpdate(DocumentEvent e) {
          update();
      }
      public void insertUpdate(DocumentEvent e) {
          update();
      }

      // your method that handles any Document change event
      public void update() {
          if( aspectCheckBox1.isSelected() ) {

            // parse the width and height, 
            // constrain the height to the aspect ratio and update it here              
          }
      }

    });            

You'd then add a similar DocumentListener to your heightTextField.

Note that if you use JTextFields you need to parse their contents, read the units (where applicable) and handle NumberFormatExceptions in the case where the user enters invalid numeric values.

To answer your question about where to add your handlers...

The update of the Width should happen when there is a Document change to the Height GUI element. Similarly the update of the Height should happen when there is a document change to the Width GUI element.

You'll need to gracefully handle divide by zero errors (or restrict input to always be greater than 0), perform your calculations using doubles and preferably use Math.round() to get the best integer values for preserving aspect.

ie:

int calculateHeight(int width, double aspect) {

    if( aspect <= 0.0 ) {
        // handle this error condition
    }
    return (int)Math.round(width / aspect); 
}

For actually tracking the aspect ratio, I would store it in a member variable and add an ActionListener to the JCheckBox... because updating the target aspect ratio on every value change of the width and height fields could result in aspect-ratio "creeping" due to integer round-off.

Here's an example on tracking your aspect every time the aspect ratio check state changes:

private double aspect = 1.0;

aspectCheckBox.addActionListener(new java.awt.event.ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent evt) {
        preserveAspectActionPerformed(evt);
    }
});    

private void preserveAspectActionPerformed(java.awt.event.ActionEvent evt) {                                           

    try {
        double w = Double.parseDouble(widthField.getText());
        double h = Double.parseDouble(heightField.getText());
        aspect = w / h;            
    } 
    catch(NumberFormatException ex) {
        // ... error occurred due to non-numeric input
        // (use a JSpinner or JFormattedTextField to avoid this)
    }
} 

The most important thing is to avoid using the wrong input type for the job:

Hope that helps you.

Upvotes: 3

MadProgrammer
MadProgrammer

Reputation: 347184

First, I wouldn't use JTextArea, it's meant for free form text editing (think NotePad). Instead you should, at the very least, use a JTextField but a JSpinner might actually even be better.

Take a look at How to Use Text Fields for more details.

Essentially, for JTextField, you could use a ActionListener and/or a FocusListener to monitor for changes to the field.

This listeners will tend to be notified after the fact, that is, only once the user has finished editing fields. If you want real time feed back, you could use a DocumentListener which will notifiy each time the underlying Document of the field is modified, in real time.

A JSpinner is a little more complicated as it's a component that contains an editor and controls. You can use a ChangeListener, which will notifiy when a change to the fields model is commited. This occurs in place of the ActionListener and FocusListener mentioned previously, so you should only require a single listener, but won't provide real time feedback (at least, not without a lot more work)

Upvotes: 3

Related Questions