Dynamic generation of buttons in Java

19,653

Solution 1

Works on my computer...

public class Panel extends JPanel {

    public Panel() {
        setLayout(new java.awt.GridLayout(4, 4));
        for (int i = 0; i < 16; ++i) {
            JButton b = new JButton(String.valueOf(i));
            b.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent e) {
                    //...
                }
            });
            add(b);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable(){
            @Override
            public void run(){
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setSize(new Dimension(300, 300));
                frame.add(new Panel());
                frame.setVisible(true);
            }
        });
    }
}

As far as I remember your version was working as well, although I had to remove your "drinking" code. Start from this example (it shows nice 4x4 grid of buttons) and determine what is wrong with your code.

Solution 2

example about validate() revalidate() plus repaint(), look like as required for correct output to the GUI, layed by some of LayourManagers

EDIT: as trashgod noticed, I added Schedule a job for the EDT

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;

    public class ValidateRevalidateRepaint {

        private JPanel panel;
        private GridBagConstraints gbc;
        private boolean validate, revalidate, repaint;

        public ValidateRevalidateRepaint() {
            validate = revalidate = repaint = false;
            panel = new JPanel(new GridBagLayout());
            gbc = new GridBagConstraints();
            gbc.insets = new Insets(0, 20, 0, 20);
            panel.add(getFiller(), gbc);
            JFrame f = new JFrame();
            f.setJMenuBar(getMenuBar());
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.getContentPane().add(panel);
            f.getContentPane().add(getRadioPanel(), "East");
            f.getContentPane().add(getCheckBoxPanel(), "South");
            f.setSize(400, 200);
            f.setLocation(200, 200);
            f.setVisible(true);
        }

        private JMenuBar getMenuBar() {
            JMenu menu = new JMenu("change");
            ActionListener l = new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    JMenuItem item = (JMenuItem) e.getSource();
                    int n = Integer.parseInt(item.getActionCommand());
                    makeChange(n);
                }
            };
            for (int j = 1; j < 5; j++) {
                String s = String.valueOf(j) + " component";
                if (j > 1) {
                    s += "s";
                }
                JMenuItem item = new JMenuItem(s);
                item.setActionCommand(String.valueOf(j));
                item.addActionListener(l);
                menu.add(item);
            }
            JMenuBar menuBar = new JMenuBar();
            menuBar.add(menu);
            return menuBar;
        }

        private JPanel getRadioPanel() {
            JPanel panel1 = new JPanel(new GridBagLayout());
            GridBagConstraints gbc1 = new GridBagConstraints();
            gbc1.insets = new Insets(2, 2, 2, 2);
            gbc1.weighty = 1.0;
            gbc1.gridwidth = GridBagConstraints.REMAINDER;
            ButtonGroup group = new ButtonGroup();
            ActionListener l = new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    JRadioButton radio = (JRadioButton) e.getSource();
                    int n = Integer.parseInt(radio.getActionCommand());
                    makeChange(n);
                }
            };
            for (int j = 0; j < 4; j++) {
                String s = String.valueOf(j + 1);
                JRadioButton radio = new JRadioButton(s);
                radio.setActionCommand(s);
                radio.addActionListener(l);
                group.add(radio);
                panel1.add(radio, gbc1);
            }
            return panel1;
        }

        private JPanel getCheckBoxPanel() {
            final String[] operations = {"validate", "revalidate", "repaint"};
            ActionListener l = new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    JCheckBox checkBox = (JCheckBox) e.getSource();
                    String ac = checkBox.getActionCommand();
                    boolean state = checkBox.isSelected();
                    if (ac.equals("validate")) {
                        validate = state;
                    }
                    if (ac.equals("revalidate")) {
                        revalidate = state;
                    }
                    if (ac.equals("repaint")) {
                        repaint = state;
                    }
                }
            };
            JPanel panel2 = new JPanel();
            for (int j = 0; j < operations.length; j++) {
                JCheckBox check = new JCheckBox(operations[j]);
                check.setActionCommand(operations[j]);
                check.addActionListener(l);
                panel2.add(check);
            }
            return panel2;
        }

        private void makeChange(int number) {
            panel.removeAll();
            for (int j = 0; j < number; j++) {
                panel.add(getFiller(), gbc);
            }
            if (validate) {
                panel.validate();
            }
            if (revalidate) {
                panel.revalidate();
            }
            if (repaint) {
                panel.repaint();
            }
        }

        private JPanel getFiller() {
            JPanel panel3 = new JPanel();
            panel3.setBackground(Color.red);
            panel3.setPreferredSize(new Dimension(40, 40));
            return panel3;
        }

        public static void main(String[] args) {//added Schedule a job for the EDT
        javax.swing.SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                ValidateRevalidateRepaint rVR = new ValidateRevalidateRepaint();
            }
        });
    }
}

Solution 3

You can use revalidate(), as shown in this example.

Addendum: Here's my variation on @mKorbel's interesting answer that shows a similar result for GridLayout. It looks like repaint() may be necessary after revalidate().

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/** @see https://stackoverflow.com/questions/6395105 */
public class ValidateRevalidateRepaint {

    private JPanel center;
    private boolean validate = false;
    private boolean revalidate = true;
    private boolean repaint = true;

    public ValidateRevalidateRepaint() {
        center = new JPanel(new GridLayout(1, 0, 10, 10));
        JFrame f = new JFrame();
        f.setTitle("VRR");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(center, BorderLayout.CENTER);
        f.add(getRadioPanel(), BorderLayout.EAST);
        f.add(getCheckBoxPanel(), BorderLayout.SOUTH);
        makeChange(4);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private JPanel getRadioPanel() {
        JPanel panel = new JPanel(new GridLayout(0, 1));
        ButtonGroup group = new ButtonGroup();
        ActionListener l = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JRadioButton radio = (JRadioButton) e.getSource();
                int n = Integer.parseInt(radio.getActionCommand());
                makeChange(n);
            }
        };
        for (int j = 0; j < 4; j++) {
            String s = String.valueOf(j + 1);
            JRadioButton radio = new JRadioButton(s);
            radio.setActionCommand(s);
            radio.addActionListener(l);
            group.add(radio);
            panel.add(radio);
            if (j == 3) {
                group.setSelected(radio.getModel(), true);
            }
        }
        return panel;
    }

    private JPanel getCheckBoxPanel() {
        final String[] operations = {"validate", "revalidate", "repaint"};
        ActionListener l = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JCheckBox checkBox = (JCheckBox) e.getSource();
                String ac = checkBox.getActionCommand();
                boolean state = checkBox.isSelected();
                if (ac.equals("validate")) {
                    validate = state;
                }
                if (ac.equals("revalidate")) {
                    revalidate = state;
                }
                if (ac.equals("repaint")) {
                    repaint = state;
                }
            }
        };
        JPanel panel = new JPanel();
        for (int j = 0; j < operations.length; j++) {
            JCheckBox check = new JCheckBox(operations[j]);
            if (j == 0) {
                check.setSelected(false);
            } else {
                check.setSelected(true);
            }
            check.setActionCommand(operations[j]);
            check.addActionListener(l);
            panel.add(check);
        }
        return panel;
    }

    private void makeChange(int number) {
        center.removeAll();
        for (int j = 0; j < number; j++) {
            center.add(getFiller());
        }
        if (validate) {
            center.validate();
        }
        if (revalidate) {
            center.revalidate();
        }
        if (repaint) {
            center.repaint();
        }
    }

    private JPanel getFiller() {
        JPanel panel = new JPanel();
        panel.setBorder(BorderFactory.createLineBorder(Color.blue, 5));
        panel.setBackground(Color.red);
        panel.setPreferredSize(new Dimension(50, 50));
        return panel;
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ValidateRevalidateRepaint();
            }
        });
    }
}
Share:
19,653
Kevin Lacquement
Author by

Kevin Lacquement

I used to be a professional programmer, but economic realities had me move on some time ago. I'm now putting short stories online while sailing with the Royal Canadian Navy to pay my bills.

Updated on June 29, 2022

Comments

  • Kevin Lacquement
    Kevin Lacquement almost 2 years

    I am trying to dynamically generate a form. Basically, I want to load a list of items for purchase, and generate a button for each. I can confirm that the buttons are being generated with the debugger, but they aren't being displayed. This is inside a subclass of JPanel:

    private void generate() {
        JButton b = new JButton("height test");
        int btnHeight = b.getPreferredSize().height;
        int pnlHeight = this.getPreferredSize().height;
        int numButtons = pnlHeight / btnHeight;
    
        setLayout(new GridLayout(numButtons, 1));
    
        Iterator<Drink> it = DrinkMenu.iterator();
    
        for (int i = 0; i <= numButtons; ++i) {
            if (!it.hasNext()) {
                break;
            }
            final Drink dr = it.next();
            b = new DrinkButton(dr);
            b.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    order.addDrink(dr);
            }});
            add(b);
        }
        revalidate();
    }
    

    DrinkButton is a subclass of JButton. Any ideas?

    • RMT
      RMT about 13 years
      Why are you setting b to a JButton then lower down setting it again to DrinkButton
    • mre
      mre about 13 years
      make sure that these modifications are happening in the Event Dispatch Thread
    • camickr
      camickr about 13 years
      The basic code looks fine. You are using revalidate() which is a key. But we don't know the context of how this code is being used. We don't if this panel has actually been added to the frame. Post your SSCCE (sscce.org) that demonstrates the problem.
  • mre
    mre about 13 years
    you should really create the JFrame and etc. in the EDT
  • Tomasz Nurkiewicz
    Tomasz Nurkiewicz about 13 years
    @mre: I didn't know about that, I just picked the first Swing tutorial I found to make the code running, it's ages since I last used this framework. I appreciate your edit, thanks!
  • trashgod
    trashgod about 13 years
    Good example, but it doesn't really address the dynamic requirement.
  • mKorbel
    mKorbel about 13 years
    I can't resist ..., :-) as I saw a few times, you really must hate repaint(), isn't it :-), your example is OP's correct base of/way, +1
  • trashgod
    trashgod about 13 years
    @mKorbel: I value your observations; always tell me if you see a place where repaint() would be better.
  • mKorbel
    mKorbel about 13 years
    @ trashgod that's about my fast hands, my mistake, If I read that again (big stupidity I wrote here), one big sorry man, correctly meant "revalidate()" + repaint(), I place that for all coumpound/composite JComponents f.e. JComboBox (often me totally angry), back to my question, why did'n use repaint();
  • trashgod
    trashgod about 13 years
    @mKorbel: Ah, if I understand correctly, revalidate() doesn't always require a subsequent repaint(); for example.
  • mKorbel
    mKorbel about 13 years
    @ trashgod yes, that's sometimes own risk required/non-required that GUI for refreshing, because we missed clear descriptions from API how/when/why (re)validate reqired repaint()
  • mKorbel
    mKorbel about 13 years
    @ trashgod I dirty hijack this thread :-), look like as not true at all cases, see my post
  • trashgod
    trashgod about 13 years
    Very interesting, but don't neglect the event dispatch thread. :-)
  • mKorbel
    mKorbel about 13 years
    @trashgod :-) because I still sure that, Action is one of ThreadSafe from Listeners :-)
  • trashgod
    trashgod about 13 years
    Sadly, the thread-safe API promises are dwindling, e.g. append() versus append().
  • mKorbel
    mKorbel about 13 years
    @trashgod and for Java7's Swing they are planed to remove single-treading rules for EDT, that's looks like as good idea at first sight, but with current Java owner(s) ....
  • Kevin Lacquement
    Kevin Lacquement about 13 years
    It turns out that my problem was threading - I didn't invokeLater().