-1

The following example creates a JFrame with JButton, JTextField and JLabel. When the button is pressed it increments the value in the text field and label. This example also creates a 2nd JFrame that is a copy of the first. The button, text field and label is copied as well.

The issue at hand is the button on the copied frame still updates the text field and label on the original. The 'why' is fairly obvious and is because the code makes specific reference to the text field and label.

Although this isn't written in the best manner but it is a great example of the scenario in which I am addressing.

The objective is, without a major rewrite, what would be the least invasive way to have the copied button action update the copied test field and label instead of the original?

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

class ButtonTextFieldLabel extends JFrame
{
    JButton bnt1 = new JButton("B1");
    JTextField tf1 = new JTextField("1");
    JLabel lbl1 = new JLabel("100");

    public ButtonTextFieldLabel()
      {
          super("Main Frame");
          setLayout(null);

          bnt1.setBounds(50,100,120,40);
          tf1.setBounds(300,100, 80,40);
          lbl1.setBounds(200,100,80,40);
          bnt1.addActionListener(new ListenerHolder(this));

          add(bnt1);
          add(tf1);
          add(lbl1);

          setSize(500,500);
          makeCopy(this);       
          setVisible(true);
      }

    private void makeCopy(ButtonTextFieldLabel originalObj) 
    {
          JFrame copyFrame = new JFrame();
          copyFrame.setTitle("Copy of " + originalObj.getTitle());
          copyFrame.setSize(originalObj.getSize());
          copyFrame.setLocation(originalObj.getX()+100, originalObj.getY()+100);
          copyFrame.setLayout(null);

          JButton copyBnt1 = new JButton();       
          copyBnt1.setBounds(originalObj.bnt1.getBounds());
          copyBnt1.setLabel(originalObj.bnt1.getLabel());
          copyFrame.add(copyBnt1);

          for (ActionListener al : originalObj.bnt1.getActionListeners())
          {
              copyBnt1.addActionListener(al);
          }

          JTextField copyTf1 = new JTextField();
          copyTf1.setBounds(originalObj.tf1.getBounds());
          copyTf1.setText(originalObj.tf1.getText());

          JLabel copyLbl1 = new JLabel();
          copyLbl1.setBounds(originalObj.lbl1.getBounds());
          copyLbl1.setText(originalObj.lbl1.getText());

          copyFrame.add(copyBnt1); 
          copyFrame.add(copyTf1);
          copyFrame.add(copyLbl1);

          copyFrame.setVisible(true);
    }

    public void runThis() 
    {
        tf1.setText(  Integer.toString(Integer.parseInt(tf1.getText())+1)   );
        lbl1.setText(  Integer.toString(Integer.parseInt(lbl1.getText())+1)   );
    }

} 

class ListenerHolder implements  ActionListener
{
    ButtonTextFieldLabel ph;
    public ListenerHolder(ButtonTextFieldLabel ph)
    {
        this.ph = ph;       
    }
    @Override
    public void actionPerformed(ActionEvent arg0) 
    {
        ph.runThis();

    }
}

public class TestBTL
    {
      public static void main(String[] args){
        new ButtonTextFieldLabel();
    }
}
camickr
  • 321,443
  • 19
  • 166
  • 288
Unhandled Exception
  • 1,427
  • 14
  • 30

1 Answers1

2

You already know the reason for the problem -- you're copying the original ActionListener, complete with its reference to the original GUI components. The overall solution is not to copy the action listener but rather to create your GUI's to hold and maintain their own unique state. One solution is rather than try to copy components via kludge, to create a self-contained GUI object that holds and updates its own state. You can create multiple GUI's using a factory method if desired.

For example:

import java.awt.Dimension;
import java.awt.event.ActionEvent;

import javax.swing.*;

public class TestBtl2 {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            createAndDisplayFrame("Frame 1").setVisible(true);
            createAndDisplayFrame("Frame 2").setVisible(true);
        });
    }

    // Factory method
    private static JFrame createAndDisplayFrame(String text) {
        BtlPanel btlPanel = new BtlPanel();
        JFrame frame = new JFrame(text);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(btlPanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        return frame;
    }
}

class BtlPanel extends JPanel {
    private int value = 0;
    private JButton button1 = new JButton(new ButtonAction("Button 1"));
    private JLabel label1 = new JLabel("00");
    private JTextField textField1 = new JTextField("00");

    public BtlPanel() {
        textField1.setFocusable(false);
        add(button1);
        add(Box.createHorizontalStrut(20));
        add(label1);
        add(Box.createHorizontalStrut(20));
        add(textField1);

        setPreferredSize(new Dimension(300, 100));
    }

    public void incrementValue() {
        value++;
        String text = String.format("%02d", value);
        label1.setText(text);
        textField1.setText(text);
    }

    private class ButtonAction extends AbstractAction {
        public ButtonAction(String name) {
            super(name);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            incrementValue();
        }
    }

}

Side Recommendations:

  • While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
  • Check out: The Use of Multiple JFrames, Good/Bad Practice?
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Thanks, I actually follow that and it makes perfect sense, but what if there are a dozen different JFrames with each with a different number of Buttons, Textfields, Labels and Toolbar buttons? Would you create a factory method for each possible combination? – Unhandled Exception Jul 28 '18 at 17:22
  • @UnhandledException: first of all, your application should only have one main application window, in other words, only one JFrame, please see the 2nd side recommendation link above for why. Secondly, I gear my GUI creation towards creation of JPanels (see the code example above), since in this way, I can place my JPanel into a JFrame, a JDialog, another JPanel or any other location where it might be needed. Thirdly, you're asking much too broad and vague a question in your comment. Usually each JPanel stands on its own, and will have an underlying M-V-C structure. – Hovercraft Full Of Eels Jul 28 '18 at 17:31