Search code examples
javaswingjbuttonjtextfield

Add and remove button dynamically in Swing frame


I'm developing a swing application. In that I've a JFrame which add JTextfield and JButton dynamically on the button click.and remove the created components if the user clicks the same button.

In the below screen image , when user clicks ADD button new row was added, and text was changed to REMOVE like in 2nd image.

enter image description here

New Row added and previous button text changed to REMOVE.

enter image description here

Now, if I click the REMOVE button, then the newly added row has to dispose and then button has to change the text again to ADD.

I've tried till adding the components, but I stuck up with removing the newly added row.

Anyone please guide me to achieve this.

Below is my code.

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.util.ArrayList;
 import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ButtonAddDynamic implements ActionListener {

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            new ButtonAddDynamic().createAndShowGUI();
        }
    });
}

private JFrame frame;
private JPanel panel = new JPanel(new GridBagLayout());
private GridBagConstraints constraints = new GridBagConstraints();

private List fields = new ArrayList();
private List fieldButton = new ArrayList();
private List fieldFile = new ArrayList();

private static int countReport = 0;
String files = null;
int y = 2;

protected void createAndShowGUI() {
    try {
        UIManager
                .setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
    } catch (UnsupportedLookAndFeelException ex) {
        ex.printStackTrace();
    } catch (InstantiationException ex) {
        ex.printStackTrace();
    } catch (ClassNotFoundException ex) {
        ex.printStackTrace();
    } catch (IllegalAccessException ex) {
        ex.printStackTrace();
    }

    String[] labels = { "VALIDATION FORM" };
    for (String label : labels)
        addColumn(label);

    frame = new JFrame("Add Button Dynamically");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(new JScrollPane(panel));
    frame.setLocationRelativeTo(null);
    frame.setResizable(false);
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);

    // Set the default button to button1, so that when return is hit, it
    // will hit the button1

    frame.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent we) {
            System.exit(0);
        }
    });
}

private void addColumn(String labelText) {
    constraints.gridx = fields.size();
    constraints.gridy = 1;
    panel.add(new JLabel(labelText), constraints);
    constraints.gridy = 2;
    final JTextField field = new JTextField(40);
    field.setEditable(false);
    panel.add(field, constraints);
    fields.add(field);

    // constraints.gridy=3;
    constraints.gridx = fields.size() + fieldButton.size();
    final JButton button = new JButton("ADD");
    button.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent ae) {

            if (button.getText().equals("ADD")) {
                button.setText("REMOVE");
                addRowBelow();
                frame.pack();
            } else if (button.getText().equals("REMOVE")) {
                button.setText("ADD");

                frame.pack();
            }

        }
    });
    panel.add(button, constraints);
    fieldButton.add(button);
    panel.revalidate(); // redo layout for extra column
}

private void addRowBelow() {
    y++;
    constraints.gridy = y;

    // System.out.println(fields.size());
    for (int x = 0; x < fields.size(); x++) {
        constraints.gridx = x;
        final JTextField field = new JTextField(40);
        field.setEditable(false);
        panel.add(field, constraints);
        constraints.gridx = x + 1;
        final JButton button = new JButton("ADD");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                if (button.getText().equals("ADD")) {
                    button.setText("REMOVE");
                    addRowBelow();
                    frame.pack();
                } else if (button.getText().equals("REMOVE")) {
                    button.setText("ADD");

                    frame.pack();
                }
            }
        });
        panel.add(button, constraints);
    }
}

public void actionPerformed(ActionEvent ae) {
    if ("Add Another TextField and Button".equals(ae.getActionCommand())) {
        addRowBelow();
        frame.pack();
        frame.setLocationRelativeTo(null);
    }
}
}

Solution

  • Trying to use GridBagLayout is making this very complicated for you. A nested layout scheme is much easier to work with when you are doing this type of thing.

    See this MCVE:

    field listing

    I'm not sure I understood your intended functionality 100% correct but I don't think it's as important as the layouts.

    My layout scheme is as follows:

    layout scheme

    This is nice because BoxLayout will handle the vertical listing without much hullabaloo. Instead of having to wrangle with GridBagConstraints, the text field and button are contained together by a panel.

    import javax.swing.*;
    import java.awt.event.*;
    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.Font;
    import java.awt.FontMetrics;
    import java.awt.Insets;
    
    public class FieldList implements Runnable, ActionListener {
        public static void main(String... args) {
            SwingUtilities.invokeLater(new FieldList());
        }
    
        final int maxFields = 2;
    
        JFrame frame;
        JPanel listing;
    
        @Override
        public void run() {
            frame = new JFrame("Text Field Listing");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            JPanel content = new JPanel(new BorderLayout());
            content.add(new JLabel("Input Form", JLabel.CENTER), BorderLayout.NORTH);
    
            listing = new JPanel();
            listing.setLayout(new BoxLayout(listing, BoxLayout.Y_AXIS));
    
            content.add(listing, BorderLayout.CENTER);
            frame.setContentPane(content);
    
            addNewField();
    
            frame.pack();
            frame.setResizable(false);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        void addNewField() {
            FieldButtonPair field = new FieldButtonPair();
            field.button.addActionListener(this);
            listing.add(field);
            frame.pack();
        }
    
        void removeLastField() {
            listing.remove(listing.getComponentCount() - 1);
            frame.pack();
        }
    
        @Override
        public void actionPerformed(ActionEvent ae) {
            AddRemoveButton source = (AddRemoveButton)ae.getSource();
    
            if(source.state == AddRemoveButton.State.ADD) {
                if(listing.getComponentCount() < maxFields) {
                    addNewField();
                    source.setState(AddRemoveButton.State.REMOVE);
                }
            } else if(source.state == AddRemoveButton.State.REMOVE) {
                removeLastField();
                source.setState(AddRemoveButton.State.ADD);
            }
        }
    }
    
    class FieldButtonPair extends JPanel {
        JTextField field;
        AddRemoveButton button;
    
        FieldButtonPair() {
            super(new BorderLayout());
            field = new JTextField();
            add(field, BorderLayout.CENTER);
            button = new AddRemoveButton();
            add(button, BorderLayout.EAST);
        }
    
        @Override
        public Dimension getPreferredSize() {
            Dimension pref = super.getPreferredSize();
            pref.width = Math.max(480, pref.width);
            return pref;
        }
    }
    
    class AddRemoveButton extends JButton {
        enum State { ADD, REMOVE }
    
        State state = State.ADD;
    
        AddRemoveButton() {
            setText(state.name());
        }
    
        void setState(State state) {
            setText(state.name());
            this.state = state;
        }
    
        @Override
        public Dimension getPreferredSize() {
            Dimension pref = super.getPreferredSize();
    
            Font f = getFont();
            FontMetrics fm = getFontMetrics(f);
            int w = fm.stringWidth(State.REMOVE.name());
            Insets ins = getInsets();
    
            pref.width = (ins.left + w + ins.right);
            return pref;
        }
    }