I am trying to draw graphics on panels, but I am not sure how I would go about doing that.
I have tried creating a class that extends the JPanel and overrided paintComponent, and some other methods, but nothing gets rendered at all.
Here is my code:
edit:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Lesson1 extends JFrame {
private static final long serialVersionUID = -198253288329146091L;
private JPanel contentPane;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Lesson1 frame = new Lesson1();
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public Lesson1() {
contentPane = new JPanel();
setContentPane(contentPane);
JPanel panel = new JPanel() {
private static final long serialVersionUID = -5974584127539186578L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillRect(0, 0, 500, 500);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
};
contentPane.add(panel);
JPanel panel_1 = new JPanel() {
private static final long serialVersionUID = 123456789L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.PINK);
g.fillRect(0, 0, 200, 200);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
};
panel.setLayout(new BorderLayout());
panel_1.setLayout(new BorderLayout());
panel.add(panel_1);
}
}
I am not sure what I am doing wrong here, but I have tried everything I could. Thanks!
Mistake #1
contentPane.setLayout(null);
and
panel.setLayout(null);
Before you do anything else, you need to learn and understand what the layout managers actually do and why null layouts are naive and not recommended (especially when you're just learning).
Start by taking the time to go through Laying Out Components Within a Container
Remove contentPane.setLayout(null); and change panel.setLayout(null); to panel.setLayout(new BorderLayout()); and you will see an immediate change.
In fact, you really don't need panel, as the default contentPane of a JFrame is already setup to use BorderLayout.
This means you can get rid of panel.setBounds(73, 52, 231, 143);
Mistake #2
Okay, this is more of an oversight, but, when you start customising components, you need to provide sizing hints, which allows the layout managers to make decisions about how best to layout your component in relationship to other components and based on it's own internal rules.
To this end, you should, at the very least, override the getPreferredSize method (avoid the setXxxSize methods as these can lead to unexpected results if used incorrectly)
Add...
#Override
public Dimension getPreferredSize() {
return new Dimension(1001, 1001);
}
to panel_1
This now means you can do something more like...
Lesson1 frame = new Lesson1();
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
and the window will pack it self around the component (and be centred on the screen) and you can get rid of setBounds(100, 100, 450, 300);
Alright, anyways I tried but have no luck making it so I can set panels in a specific position :/
Layout management in a GUI environment is part science, part voodoo, part black magic
Pixel perfect positioning is an illusion in modern UI development. There are so many factors which go into determining "how" a component should be positioned on a single platform, let along across multiple platforms, you'd spend the rest of your live constantly adding "hacks" to account for the infinite number of possible edge case.
This is why any decent UI framework abstracts the concept, Swing uses the layout manager API for this purpose, iOS (and MacOS) use Auto layout, etc...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Lesson1 {
/**
* Launch the application.
*/
public static void main(String[] args) {
new Lesson1();
}
public Lesson1() {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.NORTHWEST;
add(new RectPane(), gbc);
gbc.gridx = 2;
gbc.anchor = GridBagConstraints.NORTHEAST;
add(new RectPane(), gbc);
gbc.gridx = 1;
gbc.gridy = 1;
gbc.anchor = GridBagConstraints.CENTER;
gbc.weightx = 1;
gbc.weighty = 1;
add(new RectPane(), gbc);
gbc.weightx = 0;
gbc.weighty = 0;
gbc.gridx = 0;
gbc.gridy = 2;
gbc.anchor = GridBagConstraints.SOUTHWEST;
add(new RectPane(), gbc);
gbc.gridx = 2;
gbc.anchor = GridBagConstraints.SOUTHEAST;
add(new RectPane(), gbc);
}
// This is just to demonstrate how the layout works when the avaliable
// space is larger then the desired space, you don't need this
// and should get rid of it and just let the layout manager calculate
// it's desired size based on it's content
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
}
public class RectPane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillRect(0, 0, 100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
}
}
Understand, GridBagLayout IS the most complex layout manager in the standard API, but it is the most flexible. You're also not confined to a single layout manager, you can use compound layouts to create very advanced interfaces
Related
I want to place some elements inside a JFrame by setLocation(x, y) but my frame is not the size I expect.
The size of my JFrame is 600 x 400 but when I run the app it looks like a JFrame of about 550 x 350.
I would like to understand why this happens, even if there are better ways to implement it.
Thanks in advance for any assistance.
My code:
public static void main(String[] args) {
JFrame mainFrame = new JFrame("Example");
mainFrame.setVisible(true);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setSize(600, 400);
mainFrame.setLayout(null);
mainFrame.setResizable(false);
JPanel p1 = new JPanel();
p1.setSize(100, 100);
p1.setLocation(0,0);
p1.setBackground(Color.BLUE);
mainFrame.add(p1);
JPanel p2 = new JPanel();
p2.setSize(100, 100);
p2.setLocation(500,300);
p2.setBackground(Color.BLUE);
mainFrame.add(p2);
}
Output:
A frame's visible content size is the frame size minus the frame's decorations insets.
So your viewable area will be 600x400 - frameDecorationInsets, and this will be variable (even across the same platform).
Instead, you should be looking at the size of the parent container, for example
Note, I'm running on MacOS, which has a different frame decoration then Windows, but this concept will still work on both ... and you get resizability
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel p1 = new JPanel();
private JPanel p2 = new JPanel();
public TestPane() {
setLayout(null);
p1.setBackground(Color.BLUE);
add(p1);
p2.setBackground(Color.BLUE);
add(p2);
}
#Override
public void doLayout() {
super.doLayout();
p1.setSize(100, 100);
p1.setLocation(0, 0);
p2.setSize(100, 100);
p2.setLocation(getWidth() - p2.getWidth(), getHeight() - p2.getHeight());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 400);
}
}
}
But...
Arguably, you shouldn't be using null layouts, you should be relying on things like JComponent#getPreferredSize and JFrame#pack to provide suitable hints about how best you want the window to be sized by default, for example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
JPanel p1 = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
};
p1.setBackground(Color.BLUE);
JPanel p2 = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
};
p2.setBackground(Color.BLUE);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.anchor = GridBagConstraints.NORTHWEST;
add(p1, gbc);
gbc.gridx = 1;
gbc.gridy = 1;
gbc.anchor = GridBagConstraints.SOUTHEAST;
add(p2, gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 400);
}
}
}
I've found myself writing up quite a few programs recently which all need to display some collection of data. So far the best looking approach I've thought of is make small JPanels which contain data on each item in the collection and put them all in a big JPanel which I then put in a JScrollPane. It works and looks just as intended but there's one issue: I can't seem to get the smaller JPanels to start at the top of the bigger JPanel.
The problem is only apparent when I've got a small number of small JPanels (green) added into the bigger JPanel (red).
Described below is the method I used to produce the above and I'd like to know if there's a better way I could do it (where the list starts at the top like it should):
I created a class which extends JPanel and in it add all data I want to display. We'll call it "SmallPanel.java". I don't set the size of it (that comes later).
In my main window's class (which extends JFrame):
private JScrollPane scrollPane;
private JPanel panel;
...
scrollPane = new JScrollPane();
getContentPane().add(scrollPane);
panel = new JPanel();
panel.setLayout(new GridBagLayout());
scrollPane.setViewportView(panel);
...
private void addPanel()
{
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = panel.getComponentCount(); //The new JPanel's place in the list
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.PAGE_START; //I thought this would do it
gbc.ipady = 130; //Set the panel's height, the width will get set to that of the container JPanel (which is what I want since I'd like my JFrames to be resizable)
gbc.insets = new Insets(2, 0, 2, 0); //Separation between JPanels in the list
gbc.weightx = 1.0;
SmallPanel smallPanel = new SmallPanel();
panel.add(smallPanel, gbc);
panel.revalidate();
panel.invalidate();
panel.repaint(); //Better safe than peeved
}
Call the addPanel() method every time I want to add a panel.
EDIT
Final solution (based on MadProgrammer's answer below):
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.BevelBorder;
public class ListPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private JPanel fillerPanel;
private ArrayList<JPanel> panels;
public ListPanel(List<JPanel> panels, int height)
{
this(panels, height, new Insets(2, 0, 2, 0));
}
public ListPanel(List<JPanel> panels, int height, Insets insets)
{
this();
for (JPanel panel : panels)
addPanel(panel, height, insets);
}
public ListPanel()
{
super();
this.fillerPanel = new JPanel();
this.fillerPanel.setMinimumSize(new Dimension(0, 0));
this.panels = new ArrayList<JPanel>();
setLayout(new GridBagLayout());
}
public void addPanel(JPanel p, int height)
{
addPanel(p, height, new Insets(2, 0, 2, 0));
}
public void addPanel(JPanel p, int height, Insets insets)
{
super.remove(fillerPanel);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = getComponentCount();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.PAGE_START;
gbc.ipady = height;
gbc.insets = insets;
gbc.weightx = 1.0;
panels.add(p);
add(p, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = getComponentCount();
gbc.fill = GridBagConstraints.VERTICAL;
gbc.weighty = 1.0;
add(fillerPanel, gbc);
revalidate();
invalidate();
repaint();
}
public void removePanel(JPanel p)
{
removePanel(panels.indexOf(p));
}
public void removePanel(int i)
{
super.remove(i);
panels.remove(i);
revalidate();
invalidate();
repaint();
}
public ArrayList<JPanel> getPanels()
{
return this.panels;
}
public static void main(String[] args)
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setMinimumSize(new Dimension(500, 500));
f.setLocationRelativeTo(null);
f.getContentPane().setLayout(new BorderLayout());
final ListPanel listPanel = new ListPanel();
for (int i = 1; i <= 10; i++)
listPanel.addPanel(getRandomJPanel(), new Random().nextInt(50) + 50);
JButton btnAdd = new JButton("Add");
btnAdd.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent paramActionEvent)
{
listPanel.addPanel(getRandomJPanel(), new Random().nextInt(50) + 50);
}
});
JButton btnRemove = new JButton("Remove");
btnRemove.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent paramActionEvent)
{
listPanel.removePanel(0);
}
});
f.getContentPane().add(btnRemove, BorderLayout.NORTH);
f.getContentPane().add(btnAdd, BorderLayout.SOUTH);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setViewportView(listPanel);
f.getContentPane().add(scrollPane, BorderLayout.CENTER);
f.setVisible(true);
}
public static JPanel getRandomJPanel()
{
JPanel panel = new JPanel();
panel.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null));
panel.add(new JLabel("This is a randomly sized JPanel"));
panel.setBackground(new Color(new Random().nextFloat(), new Random().nextFloat(), new Random().nextFloat()));
return panel;
}
}
The best solution I've found is to use VerticalLayout from the SwingLabs SwingX (which can be downloaded from here) libraries.
You "could" use a GridBagLayout with an invisible component positioned at the end, whose weighty property is set to 1, but this is a lot more additional work to manage, as you need to keep updating the x/y positions of all the components to keep it in place...
Updated with GridBagLayout example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class VerticalLayoutExample {
public static void main(String[] args) {
new VerticalLayoutExample();
}
public VerticalLayoutExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final TestPane pane = new TestPane();
JButton add = new JButton("Add");
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
pane.addAnotherPane();
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(pane));
frame.add(add, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel filler;
private int y = 0;
public TestPane() {
setBackground(Color.RED);
setLayout(new GridBagLayout());
filler = new JPanel();
filler.setOpaque(false);
GridBagConstraints gbc = new GridBagConstraints();
gbc.weighty = 1;
gbc.gridy = 0;
add(filler, gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 400);
}
public void addAnotherPane() {
JPanel panel = new JPanel(new GridBagLayout());
panel.add(new JLabel("Hello"));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = y++;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(4, 4, 4, 4);
add(panel, gbc);
GridBagLayout gbl = ((GridBagLayout)getLayout());
gbc = gbl.getConstraints(filler);
gbc.gridy = y++;
gbl.setConstraints(filler, gbc);
revalidate();
repaint();
}
}
}
This is just a concept. As camickr has pointed out, so long as you know the last component, you can adjust the GridBagConstraints of the component so that the last component which is in the list has the weighty of 1 instead...
As you can, you can override some of the things GridBagLayout does, for example, instead of using the preferred size of the panel, I've asked GridBagLayout to make it fill the HORIZONTAL width of the parent container...
You can use a vertical BoxLayout.
Just make sure the maximum size of the panel is equal to the preferred size so the panel doesn't grow.
Edit:
Since your class already has a custom panel all you need to do is override the getMaximumSize() method to return an appropriate value. Something like:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class VerticalLayoutExample2 {
public static void main(String[] args) {
new VerticalLayoutExample2();
}
public VerticalLayoutExample2() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final TestPane pane = new TestPane();
JButton add = new JButton("Add");
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
pane.addAnotherPane();
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(pane));
frame.add(add, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel filler;
private int y = 0;
public TestPane() {
setBackground(Color.RED);
setLayout(new GridBagLayout());
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setBorder( new EmptyBorder(4, 4, 4, 4) );
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 400);
}
public void addAnotherPane() {
SmallPanel panel = new SmallPanel();
panel.setLayout( new GridBagLayout() );
panel.add(new JLabel("Hello"));
add(panel);
add(Box.createVerticalStrut(4));
revalidate();
repaint();
}
}
static class SmallPanel extends JPanel
{
#Override
public Dimension getMaximumSize()
{
Dimension preferred = super.getPreferredSize();
Dimension maximum = super.getMaximumSize();
maximum.height = preferred.height;
return maximum;
}
}
}
I know you mentioned you don't want to use a lib, but you can also look at Relative Layout. It is only a single class. It can easily mimic a BoxLayout but is easier to use because you don't need to override the getMaximumSize() method or add a Box component to the panel to give the vertical spacing.
You would set it as the layout of your panel as follow:
RelativeLayout rl = new RelativeLayout(RelativeLayout.Y_AXIS);
rl.setFill( true ); // fills components horizontally
rl.setGap(4); // vertical gap between panels
yourPanel.setLayout(rl);
yourPanel.add( new SmallPanel(...) );
yourPanel.add( new SmallPanel(...) );
I am currently trying to make a little app using a jframe that has multiple jpanels. I have a couple questions about this.
There has to be a cleaner way of making an app with 16 different panels than having it all inside one class. What are some other options.
Currently I only have 3 panels. I haven't gone any further because 2 of the panels aren't reflecting my changes. They are the two panels I call using
removeAll();
add();
revalidate();
repaint();
What would be causing the other panels I am calling to be blank?
Here is a look at what I have, any advice would be great. Thanks
public class Jframetest extends JFrame {
private JPanel Home;
private JPanel masslog;
private JPanel DEH;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Jframetest frame = new Jframetest();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public Jframetest() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setBounds(100, 100, 618, 373);
Home = new JPanel();
masslog = new JPanel();
DEH = new JPanel();
Home.setBackground(new Color(255, 250, 250));
Home.setBorder(new LineBorder(Color.DARK_GRAY, 1, true));
DEH.setBackground(new Color(255, 250, 250));
DEH.setBorder(new LineBorder(Color.DARK_GRAY, 1, true));
masslog.setBackground(new Color(255, 250, 250));
masslog.setBorder(new LineBorder(Color.DARK_GRAY, 1, true));
setContentPane(Home);
Home.setLayout(null);
JButton dehbutton = new JButton("Sign in");
dehbutton.setFont(new Font("Tahoma", Font.PLAIN, 14));
dehbutton.setForeground(new Color(0, 0, 0));
dehbutton.setBackground(UIManager.getColor("Menu.selectionBackground"));
DEH.add(dehbutton);
JButton btnNewButton = new JButton("Data Entry login");
btnNewButton.setFont(new Font("Tahoma", Font.PLAIN, 14));
btnNewButton.setForeground(new Color(0, 0, 0));
btnNewButton.setBackground(UIManager.getColor("Menu.selectionBackground"));
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Home.removeAll();
Home.add(DEH);
Home.revalidate();
Home.repaint();
// JOptionPane.showMessageDialog(null, "Username/Password incorrect");
}
});
btnNewButton.setBounds(44, 214, 204, 61);
Home.add(btnNewButton);
final JButton button = new JButton("Manager and Associate login");
button.setFont(new Font("Tahoma", Font.PLAIN, 14));
button.setBackground(UIManager.getColor("EditorPane.selectionBackground"));
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Home.removeAll();
Home.add(masslog);
Home.revalidate();
Home.repaint();
}
});
button.setBounds(340, 214, 204, 61);
Home.add(button);
JTextPane txtpnEmployeeLogin = new JTextPane();
txtpnEmployeeLogin.setForeground(Color.DARK_GRAY);
txtpnEmployeeLogin.setBackground(Color.WHITE);
txtpnEmployeeLogin.setFont(new Font("Tahoma", Font.PLAIN, 34));
txtpnEmployeeLogin.setText("Employee Login");
txtpnEmployeeLogin.setBounds(181, 123, 260, 52);
Home.add(txtpnEmployeeLogin);
JLabel lblNewLabel = new JLabel("New label");
lblNewLabel.setIcon(new ImageIcon("C:\\Users\\Will and April\\Downloads\\your-logo-here.jpg"));
lblNewLabel.setBounds(427, 11, 165, 67);
Home.add(lblNewLabel);
}
}
Your mistake is using a null layout, revalidate, invalidate and validate will no longer have any significant meaning, because they are related to supporting the layout management API.
Because you've removed the layout manager, you panels no longer have anything to tell them what size or location that they should appear at, meaning when you add a new component, it has a size of 0x0 and position of 0x0
Update with example
There are many reasons why you should take advantage of the layout manager API, including automatic handling of differences between how fonts are rendered on different systems, dynamic and resizable layouts, differences in screen resolution and DPI to name a few.
It will also encourage you to separate your UI into areas of responsibility instead of trying to dump your entire UI code into a single class (yes, I've seen this done, yes, I've spent most of my career cleaning up after people who do this...)
This example makes use of CardLayout and GridBagLayout, but you should take the time to become farmiluar with the some of the others avaiable in the default JDK
import java.awt.CardLayout;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
public class FrameTest extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
FrameTest frame = new FrameTest();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public FrameTest() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final CardLayout layout = new CardLayout();
setLayout(layout);
LoginPane loginPane = new LoginPane();
add(loginPane, "login");
add(new NewLoginPane(), "newLogin");
add(new ManagerLoginPane(), "managerLogin");
loginPane.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
System.out.println(command);
if ("new".equals(command)) {
layout.show(getContentPane(), "newLogin");
} else if ("manager".equals(command)) {
layout.show(getContentPane(), "managerLogin");
}
}
});
layout.show(getContentPane(), "layout");
pack();
setLocationRelativeTo(null);
}
public class LoginPane extends JPanel {
private JTextField userName;
private JButton newButton;
private JButton managerButton;
public LoginPane() {
setBorder(new EmptyBorder(20, 20, 20, 20));
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 2;
gbc.weightx = 1;
gbc.insets = new Insets(10, 10, 10, 10);
userName = new JTextField(10);
userName.setFont(new Font("Tahoma", Font.PLAIN, 34));
add(userName, gbc);
gbc.gridx = 0;
gbc.gridy = 1;
gbc.gridwidth = 1;
gbc.weightx = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
newButton = new JButton("Sign in");
newButton.setActionCommand("new");
managerButton = new JButton("Manager and Associate login");
managerButton.setActionCommand("manager");
add(newButton, gbc);
gbc.gridx++;
add(managerButton, gbc);
}
public void addActionListener(ActionListener listener) {
newButton.addActionListener(listener);
managerButton.addActionListener(listener);
}
public void remveActionListener(ActionListener listener) {
newButton.removeActionListener(listener);
managerButton.removeActionListener(listener);
}
public String getUserName() {
return userName.getText();
}
}
public class NewLoginPane extends JPanel {
public NewLoginPane() {
setLayout(new GridBagLayout());
add(new JLabel("New Login"));
}
}
public class ManagerLoginPane extends JPanel {
public ManagerLoginPane() {
setLayout(new GridBagLayout());
add(new JLabel("Welcome overlord"));
}
}
}
There has to be a cleaner way of making an app with 16 different panels than having it all inside one class. What are some other options.
You are free to create and use as many classes as need be. So if a JPanel holds a complex bit of GUI that you may wish to re-use elsewhere, or that has its own specific and separate functionality, by all means put the code in its own class.
Currently I only have 3 panels. I haven't gone any further because 2 of the panels aren't reflecting my changes. They are the two panels I call using
removeAll();
add();
revalidate();
repaint();
Smells like you're trying to re-invent the CardLayout. Why re-invent it when you can just use it?
And yes, everything MadProgrammer says about null layout is true. You should avoid using it.
Oh, the CardLayout may work for you. I thought about using a JTabbedPane. Here are my thoughts in a code example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
public class TabbedPaneDemo extends JFrame {
public TabbedPaneDemo() {
// set the layout of the frame to all the addition of all components
setLayout(new FlowLayout());
// create a tabbed pane
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.setPreferredSize(new Dimension(500,500));
add(tabbedPane);
// create three panels to be added to this frame
JPanel redPanel = new JPanel();
JPanel greenPanel = new JPanel();
JPanel bluePanel = new JPanel();
// set the colors of the panels
redPanel.setBackground(Color.RED);
greenPanel.setBackground(Color.GREEN);
bluePanel.setBackground(Color.BLUE);
// set the preferred size of each panel
redPanel.setPreferredSize(new Dimension(150,150));
greenPanel.setPreferredSize(new Dimension(150,150));
bluePanel.setPreferredSize(new Dimension(150,150));
// add the panels to the tabbed pane
tabbedPane.addTab("Red Panel", redPanel);
tabbedPane.addTab("Green Panel", greenPanel);
tabbedPane.addTab("Blue Panel", bluePanel);
// finish initializing this window
setSize(500,500); // size the window to fit its components (i.e. panels in this case)
setLocationRelativeTo(null); // center this window
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // exit application when this window is closed
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TabbedPaneDemo().setVisible(true);
}
});
}
}
And for your other question about having 16 panels in one class:
You can have panels within panel to organize things.
You can subclass JPanel and put the subclasses in their own .java files.
You can send me an email if I can be of further assistance. kaydell#yahoo.com (I like helping people with their programming and I learn from it too.)
I have a JPanel called pnlMain with a layout set to BorderLayout. Inside the panel are three JPanel added to PAGE_START, CENTER, and PAGE_END. Now, my requirement is if the Settings button is clicked, it will display a transparent JPanel above pnlMain. This transparent panel will then contain an opaque, smaller, centered panel, that will contain the settings stuff.
I know I can do this using JLayeredPane, but looking at the tutorial it says that you can only put components of different depth using absolute positioning which I'm aware is highly discouraged.
Is there some other way to do this without using absolute positioning?
You can use the glass pane of the parent frame, which will allow you to add components to it that will appear to overlaid over the main content.
Basically, I would, create a JPanel and set it to be transparent (setOpaque(false)). I would set it's layout manager to something like GridBagLayout (as it will use the preferred size of the child component and center it automatically within it's parent container).
Onto this panel I would then add the Settings panel.
Finally, I would set the parent frame's glass pane to the first (backing) pane and make it visible.
frame.getRootPane().setGlassPane(backingPane); // Or similar
Take a look at How to use Root Panes
Updated
If you can't use the glass pane of the top level frame yourself, then you need to fake it.
This example basically uses a JLayeredPane backed by a GridBagLayout
If you add a MouseListener and KeyListener to the background pane you can consume events going to the child components.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
public class FloatingPane {
public static void main(String[] args) {
new FloatingPane();
}
public FloatingPane() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final WorkPane workPane = new WorkPane();
JButton settings = new JButton("Settings");
settings.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
workPane.toggleSettings();
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(workPane);
frame.add(settings, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class WorkPane extends JLayeredPane {
private final BackingPane backingPane;
public WorkPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
add(createLabel("Center", Color.BLUE), gbc);
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.VERTICAL;
add(createLabel("Left", Color.RED), gbc);
gbc.gridx = 2;
add(createLabel("Right", Color.GREEN), gbc);
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.gridheight = GridBagConstraints.REMAINDER;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.BOTH;
backingPane = new BackingPane();
backingPane.add(new SettingsPane());
backingPane.setVisible(false);
add(backingPane, gbc);
setLayer(backingPane, DEFAULT_LAYER + 1);
}
public void toggleSettings() {
backingPane.setVisible(!backingPane.isVisible());
}
protected JLabel createLabel(String text, Color bg) {
JLabel label = new JLabel(text);
label.setHorizontalAlignment(JLabel.CENTER);
label.setOpaque(true);
label.setBackground(bg);
return label;
}
}
public class BackingPane extends JPanel {
public BackingPane() {
setLayout(new GridBagLayout());
setOpaque(false);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(128, 128, 128, 192));
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public class SettingsPane extends JPanel {
public SettingsPane() {
setBorder(new EmptyBorder(10, 10, 10, 10));
add(new JLabel("Settings"));
}
}
}
Another solution might be to fake the entire glass pane by taking a snap shot of the current panel and using a CardLayout, flip to the settings pane, using the snap shot as the backgound image for the settings pane (which can could then apply effects to like gray scaling and bluring)
I've got two panels in my frame, and I would like to set them one under other, and this first should have size like 9/10*screen frame, and this second 1/10.
I've tried with GridLayout (2 rows and one column) but I can't set them specific size.
How should I do that?
ok maybe I will write some my code:
I am writing game - pacman, and in the first panel there is a whole game, and in this second I would like to display player info(like score, name etc.) This first I would like to set on 80% screen, and second on 20%.
What is more my frame should be resizeable and all in it, sa I have to change size of Panels(keeping this 80% to 20%) when size of frame is changing. SO that I wrote this InitComponents().
package pacman;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Toolkit;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import java.awt.Image;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
public class Pacman extends JFrame implements items
{
Image image;
public Pacman()
{
initComponents();
try {
image = ImageIO.read( Pac.class.getResourceAsStream("/img/Pac02.gif"));
} catch (Exception e) {
System.out.println("Blad prz otwieraniu " + e);
System.exit(0);
}
int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width;
int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height;
this.setLocation(screen_width/3, screen_height/3);
this.setLayout(new BorderLayout());
this.getContentPane().add(panel, BorderLayout.CENTER);
this.getContentPane().add(panel2, BorderLayout.PAGE_END);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setIconImage(image);
setTitle("..::Pacman::..");
setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setPreferredSize(new Dimension(416,438));
this.pack();
setLocationRelativeTo(null);
setVisible(true);
}
private void initComponents() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
int width = e.getComponent().getSize().width;
int height = e.getComponent().getSize().height;
panel.setSize(width, height*8/10) ;
panel2.setSize(width, height*2/10);
}
});
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Pacman();
}
});
}
}
GridLayout is the wrong layout if you want different sized components. As the javadoc states:
The container is divided into equal-sized rectangles, and one
component is placed in each rectangle.
If you've only got JPanels, you might want to consider a JSplitPane - see javadoc or tutorial.
EDIT: Based on your edit/code, a JSplitPane really looks like the solution to your problem. You can then set the divider location after creation using setDividerLocation(double) - see the javadoc - e.g.
JSplitPane split = new JSplitPane(JSplitPane.VERTICAL);
split.setTopComponent(topPanel);
split.setBottomComponent(bottomPanel);
split.setDividerLocation(0.8);
Alternatively, since it's quite hard to suggest a layout without knowing your intentions for the GUI, you should consider taking a look at the Visual Guide to layouts.
Have a look at this output :
And here is the code for that, you are not suppose to use GridLayout when you need to adjust sizes for columns/rows, under such situations comes GridBagLayout to the rescue.
import java.awt.*;
import javax.swing.*;
// http://stackoverflow.com/questions/10968853/two-jpanels-in-jframe-one-under-other
public class GridBagLayoutExample
{
private void displayGUI()
{
JFrame frame = new JFrame("GridBagLayout Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel contentPane = new JPanel();
contentPane.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.FIRST_LINE_START;
gbc.fill = GridBagConstraints.BOTH;
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1.0;
gbc.weighty = 0.9;
JPanel topPanel = new JPanel();
topPanel.setOpaque(true);
topPanel.setBackground(Color.WHITE);
contentPane.add(topPanel, gbc);
gbc.weighty = 0.1;
gbc.gridy = 1;
JPanel bottomPanel = new JPanel();
bottomPanel.setOpaque(true);
bottomPanel.setBackground(Color.BLUE);
contentPane.add(bottomPanel, gbc);
frame.setContentPane(contentPane);
frame.setSize(200, 300);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new GridBagLayoutExample().displayGUI();
}
});
}
}
Check out,
Assuming that by under you mean one Jpanel is overlapping another!
http://docs.oracle.com/javase/tutorial/uiswing/components/layeredpane.html
cheers!
You can use border layout
http://docs.oracle.com/javase/tutorial/uiswing/layout/border.html
Set the bigger panel to be CENTER, and the smaller to be PAGE_END. Set the size of the bigger panel, and the smaller panel will use what space is left.