Multiple JPanel on JFrame [duplicate] - java

Below is an example of adding two panels to a frame. Only one panel (the 2nd, red panel) appears.
Why does the first panel disappear?
import java.awt.Color;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class DisappearingPanelInFrame {
DisappearingPanelInFrame() {
JFrame f = new JFrame(this.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.add(getColoredPanel(Color.GREEN));
f.add(getColoredPanel(Color.RED));
f.pack();
f.setVisible(true);
}
private JPanel getColoredPanel(Color color) {
JPanel p = new JPanel();
p.setBackground(color);
p.setBorder(new EmptyBorder(20, 150, 20, 150));
return p;
}
public static void main(String[] args) {
Runnable r = DisappearingPanelInFrame::new;
SwingUtilities.invokeLater(r);
}
}

The default layout of a JFrame (or more specifically in this case, the content pane of the frame) is a BorderLayout.
When adding a component to a BordeLayout with no constraint, the Swing API will put the component in the CENTER.
A BorderLayout can contain exactly one component in each of the 5 layout constraints.
When a second component is added to the same (in this case CENTER) constraint of a BorderLayout, this implementation of Java will display the last component added.
As to what would be a better approach depends on the specific needs of the user interface.

When a second component is added to the same (in this case CENTER) constraint of a BorderLayout, this implementation of Java will display the last component added.
Not strictly true.
The BorderLayout will only reset the bounds (ie size and location) of the last component added to a specific constraint location. This is different from other layout managers in that they will reset the bounds of all components in the container.
In the example code the red panel was the "active" panel at the time the frame was validated by using the pack() method and therefore only its bound were set and therefore only it was painted.
For a demonstration of this process run the example below using the following steps:
Click on the "Add Panel in Center" button, nothing appears to happen even though the blue panel was added to the center.
Move the mouse over the red panel and the buttons will appear because the mouse rollover logic will cause the buttons to be repainted.
Now increase the frame width and the blue panel will appear under the red panel.
The code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class DisappearingPanelInFrame {
DisappearingPanelInFrame()
{
JButton button = new JButton ("Add Panel In Center");
button.addActionListener( new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
JPanel blue = new JPanel();
blue.setBackground( Color.BLUE );
blue.add( new JButton("Button 1") );
blue.add( new JButton("Button 2") );
Component c = (Component)e.getSource();
Window window = SwingUtilities.windowForComponent(c);
window.add(blue );
window.revalidate();
window.repaint();
}
});
JFrame f = new JFrame(this.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.add(new ColoredPanel(Color.GREEN));
//f.pack();
f.add(new ColoredPanel(Color.RED));
f.add(button, BorderLayout.SOUTH);
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new DisappearingPanelInFrame();
}
};
SwingUtilities.invokeLater(r);
}
}
class ColoredPanel extends JPanel {
ColoredPanel(Color color) {
setBackground(color);
setBorder(new EmptyBorder(20, 150, 20, 150));
}
}
When the blue panel is added to the BorderLayout and when the revalidate() is invoked the bounds of the blue panel are set.
However, because of the way Swing does ZOrder painting the blue panel is painted first and then the red panel is painted on top of the blue panel. The green panel still has a size of (0, 0) since it was never the "active" panel in the BorderLayout.CENTER when the frame was initially validated with the pack() method.
When the frame is resized, the blue panel being the "active" panel in the BorderLayout.CENTER, has its bounds adjusted, so it will now fill the extra space in the frame.
Now for another test:
pack() the frame after adding the green panel to the frame.
run the code and increase the width of the frame and the red and green frame will appear
then click the button and increase the width and now all 3 panels will appear
Bottom line is still the same:
Don't try to add multiple panels to the same constraint of a BorderLayout. If you do, then make sure you remove the previous panel or you have the potential for unexpected results.

Related

Is there a way to put JPanel on a JPanel?

I'm trying to put a JPanel inside OR on a JPanel, whichever may be the case, ultimately I just want this to work like this
As you can see on the picture, the red line is a JFrame and it has 2 JPanels inside it, on the green JPanel there are some different JPanels.
I need help with the green JPanel and the little JPanels inside it. Is there any way to make it work like this?
Any help would be greatly appreciated!
==============EDIT 1==============
So here is some code, to show you what I've done so far with the help of #hfontanez.
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
public class Main
{
public static void main(String[] args)
{
//JFrame
JFrame jframe = new JFrame();
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setSize(1920, 1080);
jframe.setResizable(false);
jframe.setLocationRelativeTo(null);
jframe.setVisible(true);
//parentJpanel - This is the main panel
JPanel parentJpanel = new JPanel();
parentJpanel.setBackground(Color.YELLOW);
parentJpanel.setSize(1920, 1080);
parentJpanel.setLayout(new BorderLayout());
//smallPanel - This is the little panel on the bottom
JPanel smallPanel = new JPanel();
smallPanel.setBackground(Color.GREEN);
smallPanel.setSize(1920, 300);
smallPanel.setLocation(0, 780);
smallPanel.setLayout(new BoxLayout(smallPanel, BoxLayout.PAGE_AXIS));
parentJpanel.add(smallPanel);
jframe.add(parentJpanel);
}
}
I expected the top part to be yellow, and the small part on the bottom to be green, yet the whoel thing turned green. What did I do wrong?
The pictured GUI is created using three panels.
The YELLOW panel is the game play area. It has no layout, no components (which define their own preferred sizes) and is custom painted, so it defines a sensible preferred size to report to the layout manager.
The GREEN panel contains controls. It uses a FlowLayout.
The RED panel uses a BorderLayout, and puts the YELLOW panel in the CENTER and the GREEN panel in the PAGE_END.
Code
This is the code that made the screenshot seen above.
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
public class GameLayout {
GameLayout() {
// The main GUI. Everything else is added to this panel
JPanel gui = new JPanel(new BorderLayout(5, 5));
gui.setBorder(new EmptyBorder(4, 4, 4, 4));
gui.setBackground(Color.RED);
// The custom painted area - it is a panel that defines its preferred size.
gui.add(new GamePanel());
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
buttonPanel.setBackground(Color.GREEN);
for (int ii = 1; ii<5; ii++) {
buttonPanel.add(new JButton("B " + ii));
}
gui.add(buttonPanel,BorderLayout.PAGE_END);
JFrame f = new JFrame("Game Layout");
f.setContentPane(gui);
f.setLocationByPlatform(true);
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
Runnable r = () -> new GameLayout();
SwingUtilities.invokeLater(r);
}
}
class GamePanel extends JPanel {
GamePanel() {
setBackground(Color.YELLOW);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 100);
}
}
You need to use a LayoutManager so when you put the JPanel inside the other JPanel it will have the correct look. If you simply put panels inside the others, the parent JPanel will use its default layout manager, which is FlowLayout.
For the look of it, it seems you need to use Border Layout for the parent (yellow) panel. For the green, you have options, but I think your best bet is to use Box Layout with a PAGE_AXIS Component Orientation.
In general, you need to be familiarized with two things: 1) Layout Managers and how they behave, and 2) the default layout behavior of JComponents.

Overlapping panels

I'm trying to make overlapping panels, but widgets below the 'topmost' panel somehow react to mouse events and repaint events and render above the top layer. For example, here I have bottom layer with lots some buttons and labels, and a top layer with up/down buttons, which is fully opaque for this test (gray background):
This uses the OverlayLayout manager to place panels on different Z levels. The buttons pop into view when hovering over them, and the labels on the right, which auto-update, also pop into view.
SSCCE:
public class Temp extends JPanel {
public static void main(String... args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setContentPane(new Temp());
f.setSize(500, 250);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
public Temp() {
super(null);
setLayout(new OverlayLayout(this));
JButton bottom = new JButton("Bottom");
JButton top = new JButton("Top");
bottom.setBounds(100, 50, 200, 100);
top.setBounds(200, 100, 200, 100);
// OverlayLayout adds components from top to bottom.
add(new JPanel(null) {{
setBackground(new Color(175, 150, 125));
add(top);
}});
add(new JPanel(null) {{
add(bottom);
}});
}
}
This is basically how I do it now. It looks like this after a bit of mouse hovering, but with hovering I can bring any of the two buttons above the other:
My question is, how can I fix this in the easiest way, or what is the standard way to handle this? Perhaps a Non-modal frame-less JDialog? One argument I have against the dialog is I rather not that the user uses keyboard shortcuts to move it around the screen.
and a top layer with up/down buttons, which is fully opaque for this test (gray background):
It doesn't make sense to have an "opaque" top panel. You will not see the buttons on the bottom panel. The top panel should be transparent and the bottom panel should be your background color.
My question is, how can I fix this
If you are referring to the fact that the buttons ZOrder keeps changing as you hover over each button then you need to override the following method on your Temp panel (ie. the panel using the OverlayLayout):
#Override
public boolean isOptimizedDrawingEnabled()
{
return false;
}
This basically says that the Temp panel may contain overlapping components so all panels need to be repainted in the proper ZOrder each time.

Change EmptyBorder space colour

Does anyone know whether its possible to change the colour of the empty space provided by the BorderFactory.createEmptyBorder() method. By default it always sets it to white - i want to set it to whatever colour the background of my JFrame is (i.e. grey).
You want an empty border that has a color? The point of an empty border is that it takes up no space. Something with no space can't have a color. Did you want a line border instead?
see BorderFactory#createLineBorder(Color color)
Apply the empty border & color (or 'not' for plain) to a panel added to the content pane.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class PlainColoredEmptyBorder {
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
// the GUI as seen by the user (without frame)
JPanel gui = new JPanel(new BorderLayout());
gui.setBorder(new EmptyBorder(20, 30, 20, 30));
JTree tree = new JTree();
tree.setVisibleRowCount(4);
gui.add(new JScrollPane(tree));
gui.setBackground(Color.BLUE);
JFrame f = new JFrame("Demo");
f.add(gui);
// Ensures JVM closes after frame(s) closed and
// all non-daemon threads are finished
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// See http://stackoverflow.com/a/7143398/418556 for demo.
f.setLocationByPlatform(true);
// ensures the frame is the minimum size it needs to be
// in order display the components within it
f.pack();
// should be done last, to avoid flickering, moving,
// resizing artifacts.
f.setVisible(true);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
Why does the cursor show up when i have an empty border but not when i use any of the other border types?
Because an empty border is the only Border that does not paint. Any other type of Border will paint over the text pane cursor.
Putting your text pane inside of a JPanel with a background color doesn't work either because your text pane will be painted after the parent JPanel.
Setting the background colour of the text field and setting it to opaque fixes this

Java: Extra space in AbsoluteLayout

Is it possible to have some extra space around the edges of a JFrame that uses AbsoluteLayout? When I have a button as the downwardsmost component on the JFrame, it gets positioned right up against the bottom edge of the JFrame window, and it looks bad. I would like to know if there's a way to add a little extra space between components and the edge of the JFrame while using AbsoluteLayout.
Suggestions:
When you add a component to a JFrame, you're actually adding it to the JFrame's contentPane. To give the contentPane a "buffer" border, consider giving it an EmptyBorder(...) with the parameters being int constants for the amount of border desired around the component.
Avoid using "absolute" layouts for anything, and especially for placing components at easy to place locations for the layout managers, such as at the bottom of the GUI.
For example, note in the GUI created in the code below how the center and bottom JPanel's don't go out to the edge of the GUI because of the empty border:
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.*;
public class ButtonAtBottom {
private static void createAndShowGui() {
JPanel bottomPanel = new JPanel();
bottomPanel.add(new JButton("Bottom Button"));
bottomPanel.setBorder(BorderFactory.createTitledBorder("Bottom Panel"));
JPanel centerPanel = new JPanel();
centerPanel.setBorder(BorderFactory.createTitledBorder("Center Panel"));
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.add(centerPanel, BorderLayout.CENTER);
mainPanel.add(bottomPanel, BorderLayout.PAGE_END);
// **** here I add the border to the mainPanel which I'll
// make into the contentPane
int eb = 25;
mainPanel.setBorder(BorderFactory.createEmptyBorder(eb, eb, eb, eb));
// don't set the preferredSize per Kleopatra, but am doing it
// here simply to make code shorter for this sscce
mainPanel.setPreferredSize(new Dimension(500, 400));
JFrame frame = new JFrame("ButtonAtBottom");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
You can use Box.createRigidArea(dimensions) to create an empty space that you can add below the button.
Set an empty border on your content panel where SIZE is the amount of padding you want.
JFrame frame = new JFrame();
JPanel panel = new JPanel(null);
panel.setBorder(BorderFactory.createEmptyBorder(SIZE,SIZE,SIZE,SIZE);
frame.setContentPane(panel);
//The rest
The arguments are for top, left, bottom and right padding so if you want different paddings on each edge, you can set it accordingly.

Layout for displaying panels dynamically with scroll bar

In java, I have been trying to create a panel that can accept other panels with a scroll bar.
I tried using gridlayout, and this works fine, except for the fact that if I only add a few panels, it grows those panels to fit the size of the parent panel.
I tried using flowlayout, but this makes the panels flow horizontally as there is a scroll bar.
How do I make it so I can add panels to the parent panel starting at the top and make them always the same size(or their preferred size).
Also, when I add panels to the parent panel after an event, they do not appear until after I move or resize the form. How do I make it repaint? calling repaint() on it did not work.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
/** This lays out components in a column that is constrained to the
top of an area, like the entries in a list or table. It uses a GridLayout
for the main components, thus ensuring they are each of the same size.
For variable height components, a BoxLayout would be better. */
class ConstrainedGrid {
ConstrainedGrid() {
final JPanel gui = new JPanel(new BorderLayout(5,5));
gui.setBorder(new EmptyBorder(3,3,3,3));
gui.setBackground(Color.red);
JPanel scrollPanel = new JPanel(new BorderLayout(2,2));
scrollPanel.setBackground(Color.green);
scrollPanel.add(new JLabel("Center"), BorderLayout.CENTER);
gui.add(new JScrollPane(scrollPanel), BorderLayout.CENTER);
final JPanel componentPanel = new JPanel(new GridLayout(0,1,3,3));
componentPanel.setBackground(Color.orange);
scrollPanel.add(componentPanel, BorderLayout.NORTH);
JButton add = new JButton("Add");
gui.add(add, BorderLayout.NORTH);
add.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ae) {
componentPanel.add(new JTextField());
gui.validate();
}
});
Dimension d = gui.getPreferredSize();
d = new Dimension(d.width, d.height+100);
gui.setPreferredSize(d);
JOptionPane.showMessageDialog(null, gui);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ConstrainedGrid cg = new ConstrainedGrid();
}
});
}
}
Assuming JScrollPane, see Sizing a Scroll Pane. For convenience, Scrollable clients such as JTable offer setPreferredScrollableViewportSize(), but you can always set the viewport's size explicitly.

Categories

Resources