Dynamic generation of buttons in Java
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();
}
});
}
}
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, 2022Comments
-
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 ofJButton
. Any ideas?-
RMT about 13 yearsWhy are you setting b to a JButton then lower down setting it again to DrinkButton
-
mre about 13 yearsmake sure that these modifications are happening in the Event Dispatch Thread
-
camickr about 13 yearsThe 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 about 13 yearsyou should really create the
JFrame
and etc. in theEDT
-
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 about 13 yearsGood example, but it doesn't really address the dynamic requirement.
-
mKorbel about 13 yearsI 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 about 13 years@mKorbel: I value your observations; always tell me if you see a place where
repaint()
would be better. -
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 about 13 years@mKorbel: Ah, if I understand correctly,
revalidate()
doesn't always require a subsequentrepaint()
; for example. -
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 about 13 years@ trashgod I dirty hijack this thread :-), look like as not true at all cases, see my post
-
trashgod about 13 yearsVery interesting, but don't neglect the event dispatch thread. :-)
-
mKorbel about 13 years@trashgod :-) because I still sure that, Action is one of ThreadSafe from Listeners :-)
-
trashgod about 13 years
-
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 about 13 yearsIt turns out that my problem was threading - I didn't
invokeLater()
.