Is this Swing behavior a bug? Mysterious BoxLayout alignment - java

I've been playing around with WindowBuilder Pro for Java Swing layouts, and encountered some strange behavior that I was eventually able to reduce down to a SSCCE. Consider the source code below:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class WBPTest2 {
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
WBPTest2 window = new WBPTest2();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public WBPTest2() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.setBackground(Color.RED);
frame.getContentPane().add(panel, BorderLayout.CENTER);
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
JPanel panel_1 = new JPanel();
panel_1.setBackground(Color.GREEN);
panel_1.setMaximumSize(new Dimension(32767, 100));
panel.add(panel_1);
panel_1.setLayout(new BoxLayout(panel_1, BoxLayout.Y_AXIS));
JPanel panel_2 = new JPanel();
panel_2.setBackground(Color.BLUE);
panel_2.setMaximumSize(new Dimension(32767, 100));
panel.add(panel_2);
panel_2.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
}
}
It gives the following layout:
For whatever reason, panel_1, the green one, floats to the right. Playing with some of the parameters in the code, I can only find two ways to completely kill the gap to the left of the green panel:
change the hgap and vgap in the panel_2 constructor to 0.
OR
change the minimumSize of panel_2 to (0,0).
Do either of those, and we get this:
Now, the question is, why on earth would either the minimumSize or the hgap for the FlowLayout in panel_2 have anything to do with preventing panel_1 from filling all the way to the left of its parent? What if I happened to want a minimumSize greater than zero, and an hgap greater than zero, for my FlowLayout in panel_2? How then would I eliminate the gap to the left of my BoxLayout in panel_1?
No matter what I do to panel_1, I cannot make it fill the whole width of its parent container (unless I edit panel_2 as described above). Ironically, if I set the alignmentX for panel_1 to RIGHT_ALIGNMENT, it actually floats to the left. But the gap still exists (just now on the right). As I fill panel_1 with content and increase its size, the gap to its right grows smaller and smaller, but never completely goes away, frustratingly.
This doesn't just happen when panel_2 is a FlowLayout... it also happens with JScrollPane and many other types... types which don't usually have the hgap parameter, which means the only way to fix the gap to the left of panel_1 is to change the new container's minimumSize to 0, which again seems silly and unrelated, and most importantly may not be my design intent.

Set alignmentX on panels 1 and 2 and it fills the screen.
It appears the two different layout managers cause the panel to set different alignment defaults.

Related

Layout Problem in Java - set a 20%opaque Panel on Bottom of JFrame

Well, actually I have a Layout problem in java Swing. I simply want to add a JPanel on the bottom of a Frame - a coding snipplet that might be done with every web based language in about 5 Minutes. Not so in Java. I tried to add a jPanel to a jFrame, that Contains a jContentPane, set the size of the jPanel to what I need and to repaint and revalidate the jFrame, as well as setting the LayOutManager to null.
Java shows me in this case a full-width jPanel, that fills my whole jFrame.
Therefore I tried another approach: I divided my jPanel in a fully transparent jPanel on top and a 20%opaque jPanel on the bottom. Still it didn't work out as expected.
Since then I tried to resize the child jPanels of my new Panel and the Panel as well and tried to repaint and revalidate the jFrame. Without any effect.
Despite of my efforts, java still shows me a full sized 20%opaque jPanel on the whole jFrame, that now contains another 20%opaque jPanel on Top.
I know that this whole problem is caused by the LayoutManager, Java useless per Default. However, it is not an option to set the LayoutManager to null or even change the LayoutManager of our jFrame, because that would lead us to refactor the whole functionality of our tiny app we worked on for several weeks.
public void showUndoPanel() {
System .out.println("Show Undo Panel");
JPanel myPanel = new JPanel(null);
JPanel glassPanel = new JPanel();
JPanel ContentPanel = new JPanel();
JLabel myJLabel = new JLabel("Great Job!");
myPanel.setBackground(new Color(255,122,122,100));
glassPanel.setSize(650, 550);
glassPanel.setBackground(new Color(255,122,122,100));
myPanel.add(glassPanel);
ContentPanel.setSize(650, 30);
ContentPanel.setBackground(new Color(255,122,122,20));
ContentPanel.add(myJLabel);
myPanel.revalidate();
myPanel.repaint();
undoPanel = myPanel;
myJFrame.add(undoPanel);
myJFrame.revalidate();
}
What I expected:
What it actually does:
Well, I solved the problem by using a BoxLayoutManager and a RigidArea. In case if anyone else may encounter that problem again in the future, I decided to provide the code for this simple solution.
public void showUndoPanel() {
System .out.println("Show Undo Panel");
JPanel myPanel = new JPanel(null);
JPanel glassPanel = new JPanel();
JPanel ContentPanel = new JPanel();
JLabel myJLabel = new JLabel("Great Job!");
myPanel.setBackground(new Color(255,255,255,0));
myPanel.setLayout(new BoxLayout(myPanel, BoxLayout.PAGE_AXIS));
glassPanel.setSize(650, 650);
glassPanel.setBounds(0,0,650,550);
glassPanel.setBackground(new Color(255,122,122,0));
myPanel.add(glassPanel);
myPanel.add(Box.createRigidArea(new Dimension(0,450)));
ContentPanel.setSize(650, 30);
ContentPanel.setBounds(0,750,650,30);
ContentPanel.setBackground(new Color(255,122,122,20));
ContentPanel.add(myJLabel);
myPanel.add(ContentPanel);
myPanel.revalidate();
myPanel.repaint();
undoPanel = myPanel;
myJFrame.add(undoPanel);
myJFrame.revalidate();
}
Now it behaves as expected:
BorderLyout would make it easier to implement.
Note the comments in the following mre:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Main {
private static JFrame myJFrame;
public static void main(String[] args) {
myJFrame = new JFrame();
myJFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
myJFrame.setLocationRelativeTo(null);
showUndoPanel();
myJFrame.pack();
myJFrame.setVisible(true);
}
public static void showUndoPanel() {
JPanel myPanel = new JPanel();
myPanel.setBackground(new Color(255,255,255,0));
myPanel.setLayout(new BorderLayout());
JPanel glassPanel = new JPanel(); //uses FlowLayout by default
//glassPanel.setSize(650, 650); //use preferred size
glassPanel.setPreferredSize(new Dimension(650, 650));
//glassPanel.setBounds(0,0,650,550); //no need to set bounds. bounds are set by the layout manager
glassPanel.setBackground(new Color(255,122,122,0));
myPanel.add(glassPanel, BorderLayout.CENTER);
JPanel contentPanel = new JPanel(); //uses FlowLayout by default
//contentPanel.setSize(650, 30);//use preferred size
contentPanel.setPreferredSize(new Dimension(650, 30));
//contentPanel.setBounds(0,750,650,30); //no need to set bounds. bounds are set by the layout manager
contentPanel.setBackground(new Color(255,122,122,20));
JLabel myJLabel = new JLabel("Great Job!");
contentPanel.add(myJLabel);
myPanel.add(contentPanel, BorderLayout.SOUTH);
myJFrame.add(myPanel);
}
}

[Miglayout]adjust the size of the container to fit the total size of all components contained, with insets and gap

How can I use MigLayout so that after pack() I can see a JFrame with a proper size to hold all its children components, with borders, insets and gaps?? Now I see some elements cut off, leaving half of the size visible but half cut off.
I just figured out how to guarantee the proper size of a Container according to the sum of size of all the contained components without hardcoding anything.
Create a JPanel panel as your working panel, instead of touching the contentPane. Just add it back to the contentPane. Don't touch the contentPane, it is the key.
Set the layout of panel without hardcoded row height, column width, etc. This may ruin the layout, because your hardcoded height may be lesser or more of it is needed, leaving some line with wrong size, and leave your last line/column half cut off.
Add your elements into panel. When adding them you can specify sizes.
Add panel back to contentPane: getContentPane().add(panel); We don't need to set the layout of contentPane.
At last, pack(), setVisible(true) as you wish. No need to setSize(), setBounds(), etc. The insets and gaps will be handled automatically by MigLayout. Viola!
A SSCCE:
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;
public class InsetsAndBorder extends JFrame {
public InsetsAndBorder() {
begin();
}
private void begin() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.setLayout(new MigLayout("insets 2 2 2 2, fillx, debug", "3[]3[]3[]3", "5[]5[]5[]5"));
JLabel label1 = new JLabel("1");
JLabel label2 = new JLabel("2");
JButton button = new JButton("No way!");
panel.add(label1, "cell 1 2, grow");
panel.add(label2, "cell 2 2, grow");
panel.add(button, "cell 0 1, grow");
getContentPane().add(panel);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
InsetsAndBorder frame = new InsetsAndBorder();
}
});
}
}

JAVA JScrollPane not showing up

I have a JPanel with scrollbar and i want to add a lot of JLabels to it. But the scrollbar doesnt work . I can not use the scrollbar and even after the panel is full it doesn't scroll . Here is my code :
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Scroll {
public static void main(String[] args) {
JPanel panel = new JPanel(new GridLayout(0,1));
JPanel p = new JPanel(new BorderLayout());
JScrollPane scroll = new JScrollPane(panel);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
p.add(scroll, BorderLayout.NORTH);
JButton but = new JButton("OK");
p.add(but, BorderLayout.SOUTH);
but.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
panel.add(new JLabel("Some random text"));
scroll.revalidate();
p.repaint();p.revalidate();
}
});
JFrame frame = new JFrame();
frame.setSize(800,200);
frame.setVisible(true);
frame.add(p);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
}
}
Your problem seems to lie with your layout managers. I think BorderLayout behaves strangely when you don't use the BorderLayout.CENTER position. I changed the line
p.add(scroll, BorderLayout.NORTH);
to
p.add(scroll, BorderLayout.CENTER);
Then, to make the text appear from the top instead of centering, I changed the layout manager for the panel component to a BoxLayout. From:
JPanel panel = new JPanel(new GridLayout(0, 1));
to
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
This seems to have given me the functionality you want. Let me know if this fixes your issues or not!
As already suggested, you should probably use JList or JTable to implement your use case.
Regarding the issue, this is because all BorderLayout constraints except of CENTER will expand to as much space as their components occupy, even if that means that they will expand out of the screen bounds (in your case the NORTH section expands to the south after each button click).
To solve this, explicitly specify the preferred size for the components which can grow indefinitely if you add them to a non-central panel section with BorderLayout:
scroll.setPreferredSize(new Dimension(-1, 100));
I use -1 for the width here to indicate that it is not important (I could use any other value), since it will be ignored by the layout manager anyway (with BorderLayout.NORTH the component is stretched horizontally to take all the available horizontal space).

Multiple panels in JFrame

The useless Layout Manager guy is back again, I just can't seem to get my head around these darn layouts and make them work the way I want.
Anyway, I want to have a JFrame which has one large panel at the top (I call it a header) which runs from the left to right side of the frame at the north part of the frame, then four panels below it, two just below the header and two below those, and finally a "footer" panel, basically the same as the header panel, only at the south part of the frame.
Like this:
I had code which had the four middle panels working fine, but the header panel just messed everything up, and I have since been testing with the demo layout manager code for GridBagLayout, GridLayout again and BoxLayout. I can't getting any to work as I want.
For the aware of you here, you will probably notice I've already had a question related to this, and if having two similar questions are not allowed, please make me aware and I will move this to my previous question and this can be closed.
public Shop() {
shopUI = new JFrame("Shop Menu");
shopUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
shopUI.setBounds(100, 100, 700, 500);
shopUI.setResizable(false);
allPanels = new JPanel();
headerPanel = new JPanel();
headerPanel.setLayout(new BorderLayout());
headerPanel.setBackground(Color.cyan);
mainPanel = new JPanel();
mainPanel.setLayout(new GridLayout(2,2));
topLeft = new JPanel();
topLeft.setBackground(Color.pink);
topRight = new JPanel();
topRight.setBackground(Color.black);
bottomLeft = new JPanel();
bottomLeft.setBackground(Color.green);
bottomRight = new JPanel();
bottomRight.setBackground(Color.blue);
footerPanel = new JPanel();
footerPanel.setLayout(new BorderLayout());
footerPanel.setBackground(Color.magenta);
mainPanel.add(topLeft);
mainPanel.add(topRight);
mainPanel.add(bottomLeft);
mainPanel.add(bottomRight);
allPanels.add(headerPanel, BorderLayout.NORTH);
allPanels.add(footerPanel, BorderLayout.SOUTH);
allPanels.add(mainPanel);
shopUI.add(allPanels);
shopUI.setVisible(true);
}
I suggest learning about border layout.
for what you want, put the header in the NORTH of the border panel, and the footer in the SOUTH. How you do your other panels depends a bit on what you want them to do; if you always want them to be the same size as each other, you can use a gridlayout for them; if not, you can use boxlayout to lay either the two pairs horizontally or the two pairs vertically, again depending on what you want them to do when the frame resizes.
I think using layout managers is simplified by deciding what you want to happen in the first place, including what happens when the frame is resized. That's a large part of what layouts are all about, anyway -- what stretches, what lines up, etc.
Anyway. what you have there looks like classic BorderLayout to me. Let us know if you need further help. Incidentally, BorderLayout is default for JFrame...
edit...
this will get more interesting when you put something in the panels...
package simpleborderlayout;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main
{
public static void main(String[] args)
{
Main main = new Main(args);
main.go();
}
public Main(String ... args)
{}
public void go()
{
JPanel headerPanel = getPanel(Color.RED);
JPanel footerPanel = getPanel(Color.BLUE);
JPanel p1 = getPanel(Color.GRAY);
JPanel p2 = getPanel(Color.GRAY);
JPanel p3 = getPanel(Color.GRAY);
JPanel p4 = getPanel(Color.GRAY);
GridLayout gridLayout = new GridLayout(2,2);
JPanel middlePanel = new JPanel(gridLayout);
middlePanel.add(p1);
middlePanel.add(p2);
middlePanel.add(p3);
middlePanel.add(p4);
JFrame mainFrame = new JFrame();
mainFrame.add(headerPanel, BorderLayout.NORTH);
mainFrame.add(middlePanel, BorderLayout.CENTER);
mainFrame.add(footerPanel, BorderLayout.SOUTH);
mainFrame.pack();
mainFrame.setVisible(true);
}
private JPanel getPanel(Color c)
{
JPanel result = new JPanel();
result.setBorder(BorderFactory.createLineBorder(c));
return result;
}
}

Strange Swing error: setting GridLayout parameters makes my JPanel disappear

Don't know if I'm making a rookie mistake, but changing the row parameter when creating a GridLayout for a JPanel in my JFrame, seems to be causing another JPanel to vanish altogether:
Here's the stripped down version of the code:
In the GridBug constructor I set up my layout and put a sub class of JPanel in BorderLayout.CENTER. This does other stuff in my original code, but here just draws a box to show it's being displayed.
Somehow the state of the bottom panel which is added to BorderLayout.PAGE_END causes the center panel to vanish
In particular, changing the GridLayout row parameters to a higher value causes the center panel to vanish, lower values work fine
The code as it is now, does not work on my computer, if I uncomment some of the code to reduce row parameters, or if I don't add the JLabel or subPanels then it works...
I have no idea what I'm doing wrong:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class GridBug extends JFrame{
static class ImagePanel extends JPanel{
#Override
public Dimension getPreferredSize(){
return new Dimension(200,200);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
System.out.println("Painting image panel...");
g.setColor(Color.CYAN);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public GridBug() {
setLayout(new BorderLayout());
/*PROBLEM CODE HERE*/
//add center image panel - does not appear depending on GridLayout settings in other panels
ImagePanel centerPanel = new ImagePanel();
add(centerPanel , BorderLayout.CENTER);
//add bottom panel
JPanel bottomPanel = new JPanel();
add(bottomPanel, BorderLayout.PAGE_END);
bottomPanel.setLayout(new GridLayout(6,0)); //doesn't work
// bottomPanel.setLayout(new GridLayout(5,0)); //works
JPanel subPanel = new JPanel();
//if I pass more than 4 or so rows as param to gridlayout,
//then imagePanel is not displayed
subPanel.setLayout(new GridLayout(4,0)); //doesn't work
// subPanel.setLayout(new GridLayout(3, 0)); //works
//if I don't add this label - works
JLabel label = new JLabel("A Label:");
subPanel.add(label);
bottomPanel.add(subPanel); //if I don't add the subPanel it works fine
/*END OF PROBLEM CODE?*/
//set window params
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,400);
setLocationRelativeTo(null);
setVisible(true);
}
public static final void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new GridBug();
}
});
}
}
Not sure the exact result you want, so I didn't even try. But the problem you're facing, is one of the reasons, you want to pack() your frame, and not setSize(). You're constricting the preferred sizes of the component. pack() respects the preferred size of all your components, and should be used, rather than setSize()
bottomPanel.setLayout(new GridLayout(6,0)); //doesn't work..
// with pack(), now it does.
pack();
//setSize(400,400); // if you increase the size it'll work also, but just pack()
You just need to work on the laying out of your components now to get your desired look :)
A more detailed explanation of your problem.
This is how you code currently looks when I set the background. Note: you can already see the setSize() is taking a toll on your top panel's preferred size (200, 200).
The top CYAN is your image panel.
The BLUE is the subPanel with 4 row. With GridLayout, all the rows will be at least the size of it largest component. In this case it's the label. You can see the blue area is 4 times the height of the label (as it should be)
Not the RED, which is the bottomPanel. This has 5 rows. The largest component is the subPanel, so the total size of the bottomPanel is the size of the subPanel x 5, as you can also see. Once you add another row, the top panel gets pushed out.

Categories