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;
}
}
});
If the user clicks removeThread before clicking addThread then results will be null.
If there is only one, added 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());
}
}
All components added to a JFrame are actually added to its content pane which, by default, is a JPanel whose [default] layout manager is BorderLayout.
Usually you should call method setVisible (of class JFrame) only after you have added all the components.
After the GUI is displayed and you add or remove components from a container (such as JPanel), you need to call method revalidate. Sometimes you also need to call method repaint after you have called method revalidate.
If you click addThreadButton many times, not all the added JPanels will be visible, hence I use JScrollPane.
The text for addThreadButton is the Unicode heavy plus symbol and the text for removeThreadButton is the heavy minus symbol.
The ActionListeners are implemented using method references.
Although not required, it is recommended to explicitly launch the event dispatch thread (EDT), which is done in method main in the above code.
Related
i have a problem with refreshing the values of my gridlayout.
So, i have a JPanel in a JFrame and in that JPanel , once i entered two values(one for rows and one for columns) and then by clicking on validate, i get a GridLayout with the previous values of JButtons.
So for exemple if I enter (2,2) i get a GridLayout of 4 JButtons and in each JButton i have an image.
So my problem here is, every time i wanna refresh the GridLayout by changing the values, it doesn’t work, the GridLayout doesn’t change, or if it change, the JButtons are inclickable.
I feel like every time i click on Validate, a new GridLayout is created on my JPanel, but the first one is still there.
I will upload two pictures, one with the normal functioning (entering values first time), and the second with the bug (entering new values).
Thanks guys.
First values
Second values
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class PagePrincipal extends JFrame implements ActionListener {
JButton Valider;
JTextField Columns;
JTextField Rows;
ArrayList<JButton> butt;
public PagePrincipal(){
getContentPane().setLayout(null); //this is not the panel that contains the GridLayout
Columns = new JTextField();
Columns.setBounds(219, 35, 197, 57);
getContentPane().add(Columns);
Columns.setColumns(10);
Rows = new JTextField();
Rows.setBounds(451, 35, 226, 57);
getContentPane().add(Rows);
Rows.setColumns(10);
Valider = new JButton();
Valider.setBackground(new Color(65, 179, 163));
Valider.setForeground(Color.WHITE);
Valider.setFont(new Font("Bookman Old Style", Font.BOLD, 20));
Valider.setBounds(704, 15, 268, 81);
Valider.setText("Validation");
Valider.addActionListener(this);
this.add(Valider);
this.setResizable(true);
this.setVisible(true);
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
#Override
public void actionPerformed(ActionEvent event) {
if (event.getSource() == Valider) {
int NbRows= Integer.parseInt(Rows.getText());
int NbColumns=Integer.parseInt(Columns.getText());
JButton button[] = new JButton[NbRows*NbColumns];
butt = new ArrayList<>();
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel botPanel = new JPanel(); //this is the panel that contains the GridLayout
botPanel.setBounds(100, 200, 1000, 400);
this.add(botPanel);
botPanel.setLayout(new GridLayout(NbRows,NbColumns));
for (int i=0; i<NbRows*NbColumns; i++){
button[i]=new JButton();
botPanel.add(button[i]);
butt.add(button[i]);
}
this.setVisible(true);
}
}
}
Again, avoid null layouts if at all possible, since they force you to create rigid, inflexible, hard to maintain GUI's that might work on one platform only. Instead, nest JPanels, each using its own layout to help create GUI's that look good, are flexible, extendable and that work.
Also, when changing components held within a container, call revalidate() and repaint() on the container after making the changes. For example, the following GUI:
Is created with the following code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class PagePrincipal2 extends JPanel {
public static final int MAX_ROWS = 40;
public static final int MAX_COLS = 12;
private JButton validatorButton = new JButton("Validate");
private JSpinner columnsSpinner = new JSpinner(new SpinnerNumberModel(2, 1, MAX_COLS, 1));
private JSpinner rowsSpinner = new JSpinner(new SpinnerNumberModel(2, 1, MAX_ROWS, 1));
private List<JButton> buttonsList = new ArrayList<>();
private JPanel gridPanel = new JPanel();
public PagePrincipal2() {
JPanel topPanel = new JPanel();
topPanel.add(new JLabel("Columns:"));
topPanel.add(columnsSpinner);
topPanel.add(Box.createHorizontalStrut(10));
topPanel.add(new JLabel("Rows:"));
topPanel.add(rowsSpinner);
topPanel.add(Box.createHorizontalStrut(10));
topPanel.add(validatorButton);
JScrollPane scrollPane = new JScrollPane(gridPanel);
int gridWidth = 1000;
int gridHeight = 600;
scrollPane.setPreferredSize(new Dimension(gridWidth, gridHeight));
setLayout(new BorderLayout());
add(topPanel, BorderLayout.PAGE_START);
add(scrollPane, BorderLayout.CENTER);
validatorButton.addActionListener(e -> validateGrid());
}
private void validateGrid() {
int nbRows = (int) rowsSpinner.getValue();
int nbColumns = (int) columnsSpinner.getValue();
gridPanel.removeAll();
buttonsList.clear();
gridPanel.setLayout(new GridLayout(nbRows, nbColumns));
for (int i = 0; i < nbRows * nbColumns; i++) {
int column = i % nbColumns;
int row = i / nbColumns;
String text = String.format("[%02d, %02d]", column, row);
JButton button = new JButton(text);
button.addActionListener(e -> gridButtonAction(column, row));
buttonsList.add(button);
gridPanel.add(button);
}
gridPanel.revalidate();
gridPanel.repaint();
}
private void gridButtonAction(int column, int row) {
String message = String.format("Button pressed: [%02d, %02d]", column, row);
String title = "Grid Button Press";
int type = JOptionPane.INFORMATION_MESSAGE;
JOptionPane.showMessageDialog(this, message, title, type);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
PagePrincipal2 mainPanel = new PagePrincipal2();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
Note that the gridPanel, the one holding the buttons, is placed into a JScrollPane:
JScrollPane scrollPane = new JScrollPane(gridPanel);
Note that the main JPanel that holds everything is given a BorderLayout, and then 2 components are added, a topPanel JPanel that holds labels, buttons and fields for data input, added at the BorderLayout.PAGE_START, the top position, and the JScrollPane is added to the main JPanel at the BorderLayout.CENTER position:
setLayout(new BorderLayout());
add(topPanel, BorderLayout.PAGE_START);
add(scrollPane, BorderLayout.CENTER);
When the old buttons are removed from the gridPanel, and then new buttons are added, I will call revalidate() and repaint() on the gridPanel, the first method to get the layout managers to layout the new components, and the second method call to remove any dirty pixels that may be present:
private void validateGrid() {
int nbRows = (int) rowsSpinner.getValue();
int nbColumns = (int) columnsSpinner.getValue();
gridPanel.removeAll();
buttonsList.clear();
gridPanel.setLayout(new GridLayout(nbRows, nbColumns));
for (int i = 0; i < nbRows * nbColumns; i++) {
int column = i % nbColumns;
int row = i / nbColumns;
String text = String.format("[%02d, %02d]", column, row);
JButton button = new JButton(text);
button.addActionListener(e -> gridButtonAction(column, row));
buttonsList.add(button);
gridPanel.add(button);
}
gridPanel.revalidate();
gridPanel.repaint();
}
I am trying to add 24 JButtons to the GridLayout of my JPanel buttonPanel, but when I run it, I see that no buttons are added. (At least, they are not visible!). I tried giving the buttonPanel a background color, and it
was visible.
Does anyone know what I'm doing wrong?
This is my code (there is an other class):
package com.Egg;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class NormalCalc implements ActionListener {
static JPanel normalCalcPanel = new JPanel();
JPanel buttonPanel = new JPanel(new GridLayout(6,4,10,10));
Font font = new Font("Monospaced Bold", Font.BOLD, 18);
JButton powB = new JButton("^");
JButton sqrtB = new JButton("sqrt(");
JButton hOpenB = new JButton("(");
JButton hCloseB = new JButton(")");
JButton delB = new JButton("DEL");
JButton acB = new JButton("AC");
JButton mulB = new JButton("*");
JButton divB = new JButton("/");
JButton addB = new JButton("+");
JButton minB = new JButton("-");
JButton decB = new JButton(".");
JButton equB = new JButton("=");
JButton negB = new JButton("(-)");
JButton[] numberButtons = new JButton[10];
JButton[] functionButtons = new JButton[13];
public NormalCalc() {
functionButtons[0] = powB;
functionButtons[1] = delB;
functionButtons[2] = acB;
functionButtons[3] = sqrtB;
functionButtons[4] = mulB;
functionButtons[5] = divB;
functionButtons[6] = hOpenB;
functionButtons[7] = addB;
functionButtons[8] = minB;
functionButtons[9] = hCloseB;
functionButtons[10] = decB;
functionButtons[11] = negB;
functionButtons[12] = equB;
for (int i=0; i<10; i++) {
numberButtons[i] = new JButton(String.valueOf(i));
numberButtons[i].setFocusable(false);
numberButtons[i].setFont(font);
numberButtons[i].addActionListener(this);
}
for (int i=0; i <13; i++) {
functionButtons[i].setFocusable(false);
functionButtons[i].setFont(font);
functionButtons[i].addActionListener(this);
}
normalCalcPanel.setBounds(0, 0, 500, 700);
buttonPanel.setBounds(50, 200, 400, 400);
// Adding the buttons;
buttonPanel.add(functionButtons[0]);
buttonPanel.add(numberButtons[7]);
buttonPanel.add(numberButtons[8]);
buttonPanel.add(numberButtons[9]);
buttonPanel.add(functionButtons[1]);
buttonPanel.add(functionButtons[2]);
buttonPanel.add(functionButtons[3]);
buttonPanel.add(numberButtons[4]);
buttonPanel.add(numberButtons[5]);
buttonPanel.add(numberButtons[6]);
buttonPanel.add(functionButtons[4]);
buttonPanel.add(functionButtons[5]);
buttonPanel.add(functionButtons[6]);
buttonPanel.add(numberButtons[1]);
buttonPanel.add(numberButtons[2]);
buttonPanel.add(numberButtons[3]);
buttonPanel.add(functionButtons[7]);
buttonPanel.add(functionButtons[8]);
buttonPanel.add(functionButtons[9]);
buttonPanel.add(numberButtons[0]);
buttonPanel.add(functionButtons[10]);
buttonPanel.add(functionButtons[11]);
buttonPanel.add(functionButtons[12]);
buttonPanel.repaint();
normalCalcPanel.add(buttonPanel);
normalCalcPanel.add(MainMath.exitButton);
MainMath.frame.add(normalCalcPanel);
MainMath.frame.repaint();
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
Other (main) class:
package com.Egg;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class MainMath implements ActionListener {
static JFrame frame = new JFrame("Math Tools");
static JButton exitButton = new JButton("Exit");
JPanel mainPanel = new JPanel();
JLabel mainLabel = new JLabel("Select your tool.", SwingConstants.CENTER);
JButton nB = new JButton("Normal Calc.");
JButton tB = new JButton("Right Triangle Calc.");
JButton eB = new JButton("Equations Calc.");
public MainMath() {
frame.setBounds(300, 300, 500, 700);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.setResizable(false);
mainPanel.setLayout(null);
mainPanel.setBounds(0, 0, 500, 700);
mainLabel.setBounds(100, 25, 300, 50);
mainLabel.setFont(new Font("Monospaced Bold", Font.BOLD, 18));
nB.setBounds(100, 100, 300, 40);
tB.setBounds(100, 150, 300, 40);
eB.setBounds(100, 200, 300, 40);
nB.setFocusable(false);
tB.setFocusable(false);
eB.setFocusable(false);
nB.addActionListener(this);
tB.addActionListener(this);
eB.addActionListener(this);
mainPanel.add(mainLabel);
mainPanel.add(nB);
mainPanel.add(tB);
mainPanel.add(eB);
frame.add(mainPanel);
frame.setVisible(true);
exitButton.setBounds(16, 10, 80, 35);
exitButton.setFocusable(false);
exitButton.addActionListener(this);
}
public static void main(String[] args) {
new MainMath();
System.out.println("");
}
public void setMainScreen() {
frame.remove(exitButton);
frame.remove(NormalCalc.normalCalcPanel);
frame.add(mainPanel);
frame.repaint();
}
public static JFrame getFrame() {
return frame;
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == exitButton)
setMainScreen();
if (e.getSource() == nB) {
frame.remove(mainPanel);
new NormalCalc();
}
if (e.getSource() == tB)
new TriangleCalc();
if (e.getSource() == eB)
new EquationCalc();
}
}
I see multiple issues in your code:
Your arrangement of your buttons is weird looking at first glance.
normalCalcPanel is static, why? It should never be
setBounds(...) when using layout managers, those statements are ignored, so no need for them
repaint() unless you've added / removed any element AFTER you've displayed your GUI, these calls are unnecessary, and should come with revalidate() as well.
MainMath.exitButton another static component, STOP
MainMath.frame.add(normalCalcPanel) implies that your JFrame called frame is static as well, which again, shouldn't be
And (I'm gonna guess here) more than probably you're creating a second instance inside MainMath, but because we don't have the code from that class we don't know what's going on
I ran your code and it looks ok, so that's why I believe your MainMath is creating another instance of JFrame
Here's an example of a calculator arrangement, that should help you to structure your code similarly, not the GUI but the components and classes
Edit
Okay, I understand now that I should not use static things, but how can I add a panel to a frame which I created in another class? I used 'static', because it seemed to work
Let's make an analogy for this case, imagine that your JFrame is a book, and that your JPanels are the sheets of that book, when you write on those sheets, you don't add / paste the book to the sheets, you add the sheets to the book.
So, in this case it's the same, your main class should create the JPanels and add those to your JFrame, your JPanel classes shouldn't have knowledge of your JFrame
In the case of the book, your sheets don't need to know to which book the belong, you know that and the book knows which sheets belong to it, not viceversa.
I made an example to show you what I mean by this, I didn't recreate your GUI but made it as simple as I could for you to get the idea:
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class AnotherCalculator {
private JFrame frame;
private CalculatorPanel calculatorPanel;
public static void main(String[] args) {
SwingUtilities.invokeLater(new AnotherCalculator()::createAndShowGUI);
}
private void createAndShowGUI() {
frame = new JFrame(getClass().getSimpleName());
calculatorPanel = new CalculatorPanel();
frame.add(calculatorPanel);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class CalculatorPanel extends JPanel {
private String[] operations = {"+", "-", "*", "/", "DEL", "AC", "(", ")", "-", "^", "SQRT", ".", "+/-", "="};
private static final int NUMBER_OF_DIGITS = 10;
private JButton[] buttons;
public CalculatorPanel() {
this.setLayout(new GridLayout(0, 4, 5, 5));
buttons = new JButton[operations.length + NUMBER_OF_DIGITS];
for (int i = 0; i < buttons.length; i++) {
if (i < NUMBER_OF_DIGITS) {
buttons[i] = new JButton(String.valueOf(i));
} else {
buttons[i] = new JButton(operations[i - NUMBER_OF_DIGITS]);
}
this.add(buttons[i]);
}
}
}
i have a problem with adding a specific number of buttons from my for-loop to my JPanel, i know how to add all oof them, but i want to add only 1-10 (i havent decided yet, lets go with 10).'
this is my class where i just declare what objects i want to have.
private static int cID;
private static Deck[] card;
static ArrayList<JButton> buttonList = new ArrayList<JButton>();
private JFrame f;
private JPanel p1;
private JButton button;
public boolean isEmpty() {
return cID == 0;
}
public static void main(String[] args) {
CustomDecks c = new CustomDecks();
c.deckCreator();
}```
this is my for-loop where i create 420 buttons and give them names "card" + i where i is 0 - 419, yet when i try to add card0 to my panel, it fails, why?
private void deckCreator() {
card = new Deck[25];
new ArrayList<Cards> (cSet.cards);
for(int i = 0; i < 420; i++) {
button = new JButton();
buttonList.add(button);
button.setName("card" + i);
f.add(button);
p1.add(card0);
}
f.add(p1);
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setVisible(true);
f.setExtendedState(Frame.MAXIMIZED_BOTH);
f.setUndecorated(true);
}
}
}
I'm not sure you can create a JPanel large enough to hold 420 JButtons.
Here's an example of a JButton GUI.
[
Generally, you create an application model and view separately. The model is made up of one or more plain Java classes. The view reads from the application model but doesn't update the model.
Your controller classes (ActionListener classes) update the application model and update / repaint the view.
This pattern is called the model / view / controller (MVC) pattern.
You can see in the example code below that the model is created in the view class constructor. Generally, you create the application model first, then you create the application view.
And here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
public class JButtonScrollGUI {
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new JButtonScrollGUI();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private String[] greekAlphabet;
public JButtonScrollGUI() {
this.greekAlphabet = new String[] { "alpha", "beta", "gamma", "epsilon", "zeta" };
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setTitle("Application");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createScrollPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createScrollPanel() {
JPanel panel = new JPanel(new BorderLayout());
JPanel innerPanel = createButtonPanel();
Dimension d = innerPanel.getPreferredSize();
d.width += 50;
d.height /= 2;
panel.setPreferredSize(d);
JScrollPane scrollPane = new JScrollPane(innerPanel);
panel.add(scrollPane, BorderLayout.CENTER);
return panel;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new GridLayout(0, 3, 10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
for (int i = 0; i < 20; i++) {
JButton button1 = new JButton("Previous " + i);
panel.add(button1);
JComboBox<String> selectorBox = new JComboBox<>(greekAlphabet);
panel.add(selectorBox);
JButton button2 = new JButton("Next " + i);
button2.setPreferredSize(button1.getPreferredSize());
panel.add(button2);
}
return panel;
}
}
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class ScrollJPanelDemo extends JFrame {
public ScrollJPanelDemo(){
setSize(480, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("Select one or more options : ");
JCheckBox jcb1 = new JCheckBox("Chandigarh");
JCheckBox jcb2 = new JCheckBox("Mohali");
JCheckBox jcb3 = new JCheckBox("Delhi");
JCheckBox jcb4 = new JCheckBox("Noida");
JCheckBox jcb5 = new JCheckBox("Mumbai");
JCheckBox jcb6 = new JCheckBox("Kolkata");
//creating JPanel to hold the checkboxes
JPanel jpnl = new JPanel();
jpnl.setLayout(null);
jpnl.setOpaque(true);
jcb1.setBounds(0,0,100,40);
jcb2.setBounds(0,60,100,40);
jcb3.setBounds(0,120,100,40);
jcb4.setBounds(0,180,100,40);
jcb5.setBounds(0,240,100,40);
jcb6.setBounds(0,300,100,40);
//adding check boxes and label to the JPanel
jpnl.add(label);
jpnl.add(jcb1);
jpnl.add(jcb2);
jpnl.add(jcb3);
jpnl.add(jcb4);
jpnl.add(jcb5);
jpnl.add(jcb6);
//creating the scroll pane that will scroll the panel.
JScrollPane jscrlPane = new JScrollPane(jpnl);
jscrlPane.setBounds(0,0,300,300);
jscrlPane.setHorizontalScrollBarPolicy
(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS) ;
jscrlPane.setVerticalScrollBarPolicy
(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
//adding that scroll pane to the frame.
getContentPane().add(jscrlPane);
setVisible(true);
}
public static void main(String args[]){
new ScrollJPanelDemo();
}
}
I'm new in Java Swing and try to use of Scroll pane on my Java code, but it's not working. The Scroll Pane is add on the frame in vertical direction but not worked.
You should create your own panel that extends JPanel containing all checkboxes and in this panel override getPreferredSize() method like:
#Override
public Dimension getPreferredSize()
{
return new Dimension( 300,300 );
}
and use it in your code:
...
// creating the scroll pane that will scroll the panel.
JScrollPane jscrlPane = new JScrollPane( new MyPanelWithCheckboxes() );
jscrlPane.setBounds( 0, 0, 300, 300 );
...
I see OP already set desired/correct answer for him, yet that solution does not work with dynamic content (in my case vertically on Y axis) so I "repaired" or updated - if you will - Mateusz's original answer so that now it actually works even with dynamic JPanel content (like when you adding to it other components on the run which was my case).
Here is my code (works, using it myself):
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.JPanel;
public class JPanelForNullLayoutScroll extends JPanel {
int h;
#Override
public Dimension getPreferredSize() {
if (getComponentCount() > 0) {
h = 0;
for (Component c : getComponents()) {
h += c.getHeight();
}
} else {
h = 0;
}
// as I do not need width recount
//I set it to be taken directly from the component itself
Dimension d = new Dimension(getWidth(), h);
return d;
}
}
You can use it then in your code by implementing it like:
int tableW = 300;
int tableH = 300;
// JPANEL WITH NULL LAYOUT
JPanelForNullLayoutScroll container = new JPanelForNullLayoutScroll();
container.setLayout(null);
container.setVisible(true);
container.setEnabled(true);
container.setBounds(0, 0, tableW, tableH);
// SCROLLER
JScrollPane scrollPane = new JScrollPane(container);
scrollPane.setAlignmentX(JLabel.LEFT_ALIGNMENT);
scrollPane.getVerticalScrollBar().setUnitIncrement(8);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setBounds(0, 0, tableW, tableH);
Here is my simple example:
import java.awt.BorderLayout;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.util.ArrayList;
import java.util.Enumeration;
import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JScrollPane;
import javax.swing.JRadioButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class SelectItem extends JDialog {
private static final long serialVersionUID = 1L;
private final JPanel contentPanel = new JPanel();
private String item;
private ButtonGroup group;
/**
* Launch the application.
*/
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setBounds(100, 100, 133, 102);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BorderLayout(0, 0));
JButton btnSelectItem = new JButton("select item");
btnSelectItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ArrayList<String> items = new ArrayList<String>();
for (char c = 'A'; c <= 'Z'; c++)
items.add(String.valueOf(c));
SelectItem dialog = new SelectItem(frame, items, items.get(20));
System.out.println("Item = " + dialog.showChooseDialog());
}
});
frame.getContentPane().add(btnSelectItem);
frame.setVisible(true);
}
public String showChooseDialog(){
setVisible(true);
return item;
}
/**
* Create the dialog.
*/
public SelectItem(JFrame parent, ArrayList<String> items, String selectedItem) {
super(parent, null, Dialog.ModalityType.DOCUMENT_MODAL);
setTitle("Select Item");
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setBounds(100, 100, 450, 300);
getContentPane().setLayout(new BorderLayout());
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
getContentPane().add(contentPanel, BorderLayout.CENTER);
contentPanel.setLayout(null);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(0, 0, 434, 228);
contentPanel.add(scrollPane);
JPanel panel = new JPanel();
scrollPane.setViewportView(panel);
panel.setLayout(null);
int marginX = 6;
int currentY = 7;
int width = 420;
int height = 23;
int paddingY = 26;
int scrollY = 0;
group = new ButtonGroup();
for (String str: items){
JRadioButton rd = new JRadioButton(str);
rd.setBounds(marginX, currentY, width, height);
currentY = currentY + paddingY;
panel.add(rd);
group.add(rd);
if (str == selectedItem){ //or str.equals()...
group.setSelected(rd.getModel(), true);
//scrollY = rd.getY() - height/2 - scrollPane.getHeight()/2;
scrollY = rd.getY() + height/2 - scrollPane.getHeight()/2;
}
}
System.out.println("ScrollY: " + scrollY);
Dimension size = panel.getPreferredSize();
size.setSize(size.getWidth(), currentY);
panel.setPreferredSize(size);
//this.setVisible(true);
scrollPane.getVerticalScrollBar().setValue(scrollY);
panel.repaint();
panel.revalidate();
scrollPane.getVerticalScrollBar().repaint();
scrollPane.getVerticalScrollBar().revalidate();
scrollPane.repaint();
scrollPane.revalidate();
this.repaint();
this.revalidate();
{
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
getContentPane().add(buttonPane, BorderLayout.SOUTH);
{
JButton okButton = new JButton("OK");
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Enumeration<AbstractButton> iter = group.getElements();
while (iter.hasMoreElements()){
AbstractButton rd = iter.nextElement();
if (group.isSelected(rd.getModel())){
item = rd.getActionCommand();
break;
}
}
//System.out.println(group.getSelection().getActionCommand());
setVisible(false);
dispose();
}
});
okButton.setActionCommand("OK");
buttonPane.add(okButton);
getRootPane().setDefaultButton(okButton);
}
{
JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
item = null;
setVisible(false);
dispose();
}
});
cancelButton.setActionCommand("Cancel");
buttonPane.add(cancelButton);
}
}
}
}
I create a list of String and then pass it to a JDialog constructor with a default string value, or selectedItem as in my code.
This dialog will display all item of the list and let the user choose one.
This is done by using JRadioButton, and the JRadioButton object with it value equals to default value will be selected by default.
Everything work fine. But I want to scroll the panel to that radio-button automatically when a dialog is open, that radio-button will be in middle of vertical alignment
Like this:
.
if (str == selectedItem){ //or str.equals()...
group.setSelected(rd.getModel(), true);
scrollY = rd.getY() + height/2 - scrollPane.getHeight()/2;
}
...
scrollPane.getVerticalScrollBar().setValue(scrollY);
But when the dialog isopened, it doesn't scroll to that position.
I know that something hasn't been updated because a dialog are not visible.
Try adding
this.setVisible(true);
before update the scrollbar, the dialog will be open twice, and in the second time, it display correctly as I want.
But i still don't know how to solve this problem.
Anyone can help me. Thanks.
(sorry for my bad grammar)
First of all:
Get rid of all the repaint() and revalidate() methods. The only time you need to use those methods is when you add/remove components from a visible GUI. In that case the order is revalidate() (to invoke the layout manager) and then repaint() (to paint the components at there new size/location).
Get rid of null layouts. Scrolling works better when you use layout managers and each component determines it own size.
Regarding your problem the maximum value of the scrollbar is only 100 at the time you execute your code, so you can't set the value to 240. I would guess this is because you have not used the pack() method on the dialog before you make it visible. You should be able to set the value of the scrollbar after the pack().
Or, maybe a better approach is the use panel.scrollRectToVisible(....). This seems to work even if you don't pack() the dialog.