I am trying to make a basic program where whenever you press a button, a JCheckbox is generated and added below the other JCheckbox on a panel. I figured out how to generate the JCheckbox with a ActionListener but I can't figure out how to get each new check box to appear below the previous one. Everything else seems to be working but I can't get this location thing to work.
box.setVisible(true);
_p.add(box);
int i = 0;
int u = i++;
box.setAlignmentX(0);
box.setAlignmentY(u);
Here is a sample of my code. I've been stuck on this problem for a very long time and would greatly appreciate any and all help.
Check out the Swing tutorial on Using Layout Managers. You could use a vertical BoxLayout or a GridBagLayout or maybe a GridLayout.
Whatever layout you choose to use the basic code for adding components to a visible GUI is:
panel.add(...);
panel.revalidate();
panel.repaint();
The other statements in your code are not necessary:
//box.setVisible(true); // components are visible by default
The following methods do not set a grid position.
//box.setAlignmentX(0);
//box.setAlignmentY(u);
JCheckbox lives in a container like a JPanel (that means that you add checkbox to a panel) . A JPanel have a layoutManager. Take a look about Using Layout Managers
You could use BoxLayout with Y_AXIS orientation or a GridLayout with 1 column and n rows.
Example:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class CheckBoxTest {
private JPanel panel;
private int counter=0;
public CheckBoxTest(){
panel = new JPanel();
panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));
JButton button = new JButton(" Add checkbox ");
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent evt){
panel.add(new JCheckBox("CheckBox"+Integer.toString(counter++)));
//now tell the view to show the new components added
panel.revalidate();
panel.repaint();
//optional sizes the window again to show all the checkbox
SwingUtilities.windowForComponent(panel).pack();
}
});
panel.add(button);
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("Checkbox example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLocationByPlatform(Boolean.TRUE);
CheckBoxTest test = new CheckBoxTest();
frame.add(test.panel);
//sizes components
frame.pack();
frame.setVisible(Boolean.TRUE);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Related
I am creating a user system to hold multiple details of multiple users, so I would like to create a button that would be able to create another button. When the second button is pressed a form will open for the user to fill. I have already created the form for the user to fill but I cannot manage to make the button to create more buttons to work. I have coded this but it does not show the button on the JPanel.
I have created the following code:
private void mainButtonActionPerformed(java.awt.event.ActionEvent evt) {
JButton b=new JButton("Click Here");
b.setBounds(50,100,95,30);
jPanel3.add(b);
b.setVisible(true);
}
I want to know what is the correct code to write in the events / mouseClick of the button.
When you add or remove components from a JPanel, you need to make that JPanel redraw itself. Just adding or removing a component does not make this happen. Hence, after adding or removing a component from a JPanel, you need to call method revalidate followed by a call to repaint.
Refer to Java Swing revalidate() vs repaint()
Also note that the following line of your code is not required since the visible property is true by default.
b.setVisible(true);
Also, it is recommended to use a layout manager which means you don't need to call method setBounds as you have in this line of your code.
b.setBounds(50,100,95,30);
EDIT
As requested, a sample application. Clicking on the Add button will add another button. Note that the ActionListener for the Add button is implemented as a method reference.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ButtonAd {
private static final String ADD = "Add";
private JFrame frame;
private JPanel buttonsPanel;
private void addButton(ActionEvent event) {
JButton button = new JButton("Added");
buttonsPanel.add(button);
buttonsPanel.revalidate();
buttonsPanel.repaint();
}
private JPanel createAddButton() {
JPanel addButtonPanel = new JPanel();
JButton addButton = new JButton(ADD);
addButton.addActionListener(this::addButton);
addButtonPanel.add(addButton);
return addButtonPanel;
}
private void createAndDisplayGui() {
frame = new JFrame("Add Buttons");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createButtonsPanel(), BorderLayout.CENTER);
frame.add(createAddButton(), BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonsPanel() {
buttonsPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
buttonsPanel.setPreferredSize(new Dimension(450, 350));
return buttonsPanel;
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> new ButtonAd().createAndDisplayGui());
}
}
This is the add(main) version
This is the add(scroll) version
Im trying to get a window full of lables and make it scrollable, this is my code for that purpose:
public class JobHistoryListScreen extends JFrame implements View
{
#Override
public void showScreen()
{
setSize(800, 800);
setLayout(new BorderLayout());
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel main = new JPanel();
main.setSize(500,500);
JScrollPane scroll = new JScrollPane(main,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setSize(500,500);
//Font
//Font david50 = new Font("David", Font.BOLD, 50);
for(int i=0; i<1000; i++)
{
JLabel empty = new JLabel("No jobs to display!");
empty.setBounds(0,i+250,400,100);
empty.setFont(david50);
main.add(empty);
}
add(main);
setVisible(true);
}
public static void main(String[] args) {
JobHistoryListScreen v = new JobHistoryListScreen();
v.showScreen();
}
}
For some reason the window gets filled with the labels but is not scrollable at all.
Learn about layout managers. Refer to Laying Out Components Within a Container. Default for JPanel is FlowLayout and because the JPanel is inside a JScrollPanel, the labels will not wrap. And since you set the horizontal scroll bar policy to NEVER, there is no horizontal scroll bar and hence you cannot scroll horizontally. Try using BoxLayout to display all the labels one under the other. Alternatively you could use a GridLayout with 0 (zero) rows and 1 (one) column. Refer to the tutorial for more details.
EDIT
Here is my modified version of your code. Explanatory notes appear after the code.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.WindowConstants;
public class JobHistoryListScreen implements Runnable {
private JFrame frame;
#Override // java.lang.Runnable
public void run() {
showScreen();
}
public void showScreen() {
frame = new JFrame("Jobs");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel main = new JPanel(new GridLayout(0, 1));
JScrollPane scroll = new JScrollPane(main,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setPreferredSize(new Dimension(500, 500));
Font david50 = new Font("David", Font.BOLD, 50);
for(int i=0; i<1000; i++) {
JLabel empty = new JLabel("No jobs to display!");
empty.setFont(david50);
main.add(empty);
}
frame.add(scroll);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
JobHistoryListScreen v = new JobHistoryListScreen();
// Launch Event Dispatch Thread (EDT)
EventQueue.invokeLater(v);
}
}
I don't know what interface View is so I removed that part.
No need to extend class JFrame.
No need to explicitly call setSize() on JFrame. Better to call pack().
Default content pane for JFrame is JPanel and default layout manager for that JPanel is BorderLayout so no need to explicitly set.
No need to call setSize() on JPanel.
Call setPreferredSize() rather than setSize() on JScrollPane.
Add the JScrollPane to the JFrame and not the JPanel.
No need to call setBounds() because GridLayout handles this.
Explicitly launch EDT (Event Dispatch Thread) by calling invokeLater().
Here is a screen capture of the running app. Note the vertical scroll bar.
I am using swing in Java and I need to create a JButton and put it in a JPanel.
I reed tutorials and I did this:
public void crearNuevaMiga(String nombre)
{
JButton nuevo = new JButton(nombre);
this.MigasDePan.add(nuevo);
nuevo.setVisible(true);
nuevo.setLocation(new Point(migaX, migaY));
System.out.println(nuevo.getLocation().x + " "+ nuevo.getLocation().y);
migaX = migaX-avanceMigas;
}
I do that and when I call the function, I cant see the button. I put a button with the designer of NetBeans and get X and Y Location. Then, in the variables migaX and migaY I put that X and Y Location, so the button need to be in the same position, but it is not there.
Anyone knows why? Maybe putting the location in that way is not correct?
Thanks for your time!
EDIT: MigasDePan is my JPanel
Here's a simple example of putting a JButton in a JPanel, and putting the JPanel in a JFrame. I created this code without using any GUI builder.
I called the SwingUtilities invokeLater method in the main method to put the creation and use of the Swing components on the Event Dispatch thread. Oracle and I insist that you start every Swing application on the Event Dispatch thread.
I used a JFrame. You must call the JFrame methods in the order they are called in the run method.
I used a JPanel. I put the JButton in the middle of the JPanel, since it's the only component on the JPanel.
I used a Swing layout, the Border Layout. Different Swing layouts are used to create different Swing component layouts.
Here's the short, self-contained, runnable code.
package com.ggl.testing;
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class MyButton implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new MyButton());
}
#Override
public void run() {
JFrame frame = new JFrame("My Button");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel());
frame.pack();
frame.setVisible(true);
}
private JPanel createMainPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
JButton myButton = new JButton("My Button");
panel.add(myButton);
return panel;
}
}
By default components have a size of (0, 0) so there is nothing to paint.
When you dynamically add a button to a visible GUI you need to invoke the layout manager so the components size/location can be determined by the layout manager.
The basic code is:
panel.add(...);
panel.revalidate();
panel.repaint();
First of all, this is a more specific question than it seems to be. To start off: I am currently doing a small application with a rather small GUI, so I decided to make a GUI class, and initialize my whole GUI in this constructor.
This would look like this:
public class GUI extends JFrame{
public GUI{
//Initialize GUI here, including its Frames, Panels, Buttons etc.
}
}
How can I now access the GUIs frame etc. from an external class? If I would create an object of the GUI class, I would simply duplicate my GUI window. I did not come across any other ideas than making the frame, panel and so on static.
I'm somewhat lost right now. Also I'm pretty sure that I am not thinking the right way into this case, but I need someone to point me to the right direction. If someone could help me out, I would be very thankful.
First of all, using static is the worst solution possible, even if your GUI class is a singleton (buf if it is, at least it will work fine).
Why don't you simply create getters and/or setters ? And finally, it is usually not normal that external classes need to access the components of another graphic class. You should wonder if your design is the most fitted for your needs.
Here's a simple GUI to change the background color of a JPanel with a JButton. Generally, this is how you construct a Swing GUI.
package com.ggl.testing;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ChangeDemo implements Runnable {
private boolean isYellow;
private JFrame frame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new ChangeDemo());
}
#Override
public void run() {
frame = new JFrame("Change Background Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS));
JPanel namePanel = new JPanel();
JLabel nameLabel = new JLabel(
"Click the button to change the background color");
nameLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT);
namePanel.add(nameLabel);
mainPanel.add(namePanel);
final JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout());
buttonPanel.setBackground(Color.YELLOW);
isYellow = true;
JButton changeButton = new JButton("Change Color");
changeButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
isYellow = !isYellow;
if (isYellow) buttonPanel.setBackground(Color.YELLOW);
else buttonPanel.setBackground(Color.RED);
}
});
buttonPanel.add(changeButton);
mainPanel.add(buttonPanel);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
You don't access the Swing components of the GUI from other classes. You create other classes to hold the values of the GUI.
Generally, you use the model / view / controller pattern to construct a Swing GUI. That way, you can focus on one part of the GUI at a time.
Take a look at my article, Java Swing File Browser, to see how the MVC pattern works with a typical Swing GUI.
You don't need to make it static or to create a new JFrame object every time.
Have a look at this simple code :
class UseJFrame {
public static void main(String...args) {
Scanner sc = new Scanner(System.in);
JFrame frame = new GUI();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
System.out.println("Press E to exit");
String ip;
while(true) {
System.out.println("Show GUI (Y/N/E)? : ");
ip = sc.nextLine();
if(ip.equalsIgnoreCase("y") {
frame.setVisible(true);
} else if(ip.equalsIgnoreCase("n") {
frame.setVisible(false);
} else { // E or any other input
frame.dispose();
}
}
}
}
Note : Don't make GUI visible through constructor or it will show window at the very starting of creation of JFrame object.
If you want to use the same JFrame object at other places too then pool architecture would be better approach.
I want to put FlowLayout with lets say 5 labels inside BorderLayout as north panel (BorderLayout.NORTH), and when I resize my window/frame I want the labels to not disappear but instead move to new line.
I have been reading about min, max values and preferredLayoutSize methods. However they do not seem to help and I am still confused.
Also, I would not like to use other layout like a wrapper or something.
One of the annoying things about FlowLayout is that it doesn't "wrap" it's contents when the available horizontal space is to small.
Instead, take a look at WrapLayout, it's FlowLayout with wrapping...
The following code does exactly what you asked.
The program has a frame whose contentPane is set for BorderLayout. It contains another panel flowPanel that has a flow layout and is added to the BorderLayout.NORTH.
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PanelFun extends JFrame {
final JPanel flowPanel;
public PanelFun() {
setPreferredSize(new Dimension(300,300));
getContentPane().setLayout(new BorderLayout());
flowPanel = new JPanel(new FlowLayout());
addLabels();
getContentPane().add(flowPanel, BorderLayout.NORTH);
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
PanelFun.this.getContentPane().remove(flowPanel); //this statement is really optional.
PanelFun.this.getContentPane().add(flowPanel);
}
});
}
void addLabels(){
flowPanel.add(new JLabel("One"));
flowPanel.add(new JLabel("Two"));
flowPanel.add(new JLabel("Three"));
flowPanel.add(new JLabel("Four"));
flowPanel.add(new JLabel("Five"));
}
public static void main(String[] args) {
final PanelFun frame = new PanelFun();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
frame.setVisible(true);
}
});
}
}
So, how does it work?
The key to having the components inside flowPanel realign when the frame is resized is this piece of code
PS: Let me know if you are new to Swing and do not understand some part of the code.
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
PanelFun.this.getContentPane().remove(flowPanel);
PanelFun.this.getContentPane().add(flowPanel);
}
});
Without this code the flowPanel will not realign its components as it is not its normal behaviour to reposition components when the containing frame is resized.
However, it is also its behaviour that when flowPanel is added to a panel, it would position components as per the available space. So, if we add the flowPanel everytime the frame resizes, the inner elements will be repositioned to use the available space.
Update:
As camickr pointed out correctly, this method will not work in case you add anything to the center (BorderLayout.CENTER)