I am adding panels to a frame with a button click, each panel goes under the last added panel, I achieve that with setBounds
, each time incrementing y position. There is a second button, that is supposed to remove the latest panel that I added. It should keep removing panels after every click. I have tried a few solutions but they all failed.
My code:
public class lab3 {
static int y = 50;
static JButton addThread;
static JPanel results;
public static void main(String[] args) {
JFrame jFrame = new JFrame();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(550, 650);
jFrame.setTitle("Lab3");
jFrame.setVisible(true);
JPanel panelAddThread = new JPanel();
panelAddThread.setLayout(null);
panelAddThread.setBounds(0, 0, 550, 50);
panelAddThread.setBackground(Color.red);
JLabel threadLabel = new JLabel();
threadLabel.setText("Thread count: ");
addThread = new JButton();
addThread.setBounds(300, 0, 25, 25);
addThread.setText("+");
addThread.setBorder(null);
addThread.addActionListener(e -> {
results = new JPanel();
results.setBounds(0, y, 550, 150);
results.setBackground(Color.blue);
results.setLayout(null);
jFrame.add(results);
jFrame.repaint();
y = y+ 150;
});
// Remove
JButton removeThread = new JButton();
removeThread.setBorder(null);
removeThread.setBounds(340, 0, 25, 25);
removeThread.setText("-");
removeThread.addActionListener(e -> {
});
panelAddThread.add(threadLabel);
panelAddThread.add(removeThread);
panelAddThread.add(addThread);
jFrame.add(panelAddThread); }
}
I understand that you are asking for the code for the ActionListener for removeThread
(JButton).
results
is actually the last JPanel
that you added, so that is the JPanel
that you need to remove. However, once you remove it, you need to assign it to the new, last JPanel
, i.e. the second last JPanel
that was added.
Method getComponents returns all the components that were added, in the order that they were added. When you remove a component from a Container
and subsequently call method getComponents
, the array returned will not contain the component that you just removed. Hence the new, last JPanel
is the last element in the array returned by method getComponents
.
All that remains is to handle the "edge" cases.
Here is the code for the actionPerformed
method:
removeThread.addActionListener(e -> {
if (results != null) {
jFrame.remove(results);
jFrame.repaint();
y -= 150;
Container contentPane = jFrame.getContentPane();
Component[] cmpts = contentPane.getComponents();
int count = cmpts.length;
if (count > 1) {
results = (JPanel) cmpts[count - 1];
}
else {
results = null;
}
}
});
removeThread
before clicking addThread
then results
will be null.JPanel
and we remove it, then we need to set results
to null.For completeness, here is the entire program, including the above changes.
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class lab3 {
static int y = 50;
static JButton addThread;
static JPanel results;
public static void main(String[] args) {
JFrame jFrame = new JFrame();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(550, 650);
jFrame.setTitle("Lab3");
jFrame.setVisible(true);
JPanel panelAddThread = new JPanel();
panelAddThread.setLayout(null);
panelAddThread.setBounds(0, 0, 550, 50);
panelAddThread.setBackground(Color.red);
JLabel threadLabel = new JLabel();
threadLabel.setText("Thread count: ");
addThread = new JButton();
addThread.setBounds(300, 0, 25, 25);
addThread.setText("+");
addThread.setBorder(null);
addThread.addActionListener(e -> {
results = new JPanel();
results.setBounds(0, y, 550, 150);
results.setBackground(Color.blue);
results.setLayout(null);
jFrame.add(results);
jFrame.repaint();
y = y + 150;
});
// Remove
JButton removeThread = new JButton();
removeThread.setBorder(null);
removeThread.setBounds(340, 0, 25, 25);
removeThread.setText("-");
removeThread.addActionListener(e -> {
if (results != null) {
jFrame.remove(results);
jFrame.repaint();
y -= 150;
Container contentPane = jFrame.getContentPane();
Component[] cmpts = contentPane.getComponents();
int count = cmpts.length;
if (count > 1) {
results = (JPanel) cmpts[count - 1];
}
else {
results = null;
}
}
});
panelAddThread.add(threadLabel);
panelAddThread.add(removeThread);
panelAddThread.add(addThread);
jFrame.add(panelAddThread);
}
}
However, I would write your program differently such that it uses Swing's layout managers. Consider the following:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class LabThree implements Runnable {
private JFrame frame;
private JPanel container;
private JPanel results;
public void run() {
buildAndDisplayGui();
}
private void buildAndDisplayGui() {
frame = new JFrame("Lab 3");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createButtonsPanel(), BorderLayout.PAGE_START);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonsPanel() {
JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
buttonsPanel.setBackground(Color.red);
JButton addThreadButton = new JButton("\u2795");
addThreadButton.addActionListener(this::addThread);
buttonsPanel.add(addThreadButton);
JButton removeThreadButton = new JButton("\u2796");
removeThreadButton.addActionListener(this::removeThread);
buttonsPanel.add(removeThreadButton);
return buttonsPanel;
}
private JScrollPane createMainPanel() {
container = new JPanel();
container.setLayout(new BoxLayout(container, BoxLayout.PAGE_AXIS));
JScrollPane scrollPane = new JScrollPane(container);
scrollPane.setPreferredSize(new Dimension(570, 620));
return scrollPane;
}
private void addThread(ActionEvent event) {
results = new JPanel();
results.setBackground(Color.blue);
Dimension dim = new Dimension(550, 150);
results.setMaximumSize(dim);
results.setMinimumSize(dim);
results.setPreferredSize(dim);
container.add(results);
container.revalidate();
}
private void removeThread(ActionEvent event) {
if (results != null) {
container.remove(results);
Component[] cmpts = container.getComponents();
int count = cmpts.length;
if (count > 0) {
results = (JPanel) cmpts[count - 1];
}
else {
results = null;
}
container.revalidate();
container.repaint();
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new LabThree());
}
}
JPanel
whose [default] layout manager is BorderLayout.setVisible
(of class JFrame
) only after you have added all the components.JPanel
), you need to call method revalidate
. Sometimes you also need to call method repaint
after you have called method revalidate
.addThreadButton
many times, not all the added JPanel
s will be visible, hence I use JScrollPane.addThreadButton
is the Unicode heavy plus symbol and the text for removeThreadButton
is the heavy minus symbol.ActionListener
s are implemented using method references.main
in the above code.