Strange Swing error: setting GridLayout parameters makes my JPanel disappear - java

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.

Related

[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();
}
});
}
}

Adding JPanels to regions other than CENTER

I am currently reading chapter 12 of Head First Java about making GUIs. They have just mentioned that JFrames are split into center, north, south, east and west. The book then uses the 2 argument add() method to add specified components to a specified region with that JFrame.
I can add a JButton to each of the five regions fine. I can also add my own JPanel to the center region with JButtons all around it. But then when I try to add a JPanel to any region other than center, the JPanel does not appear.
I really have searched all over the web and Stack Overflow for the past hour and I have not come across anything that mentions adding JPanels to any region other than center in a JFrame. So my question is: is it possible to add JPanels to the north, south, east or west regions of a JFrame?
Thanks in advance to anyone that can help me with this.
Here is the code that I've been trying to run with my JPanel in the north region:
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.BorderLayout;
public class StackQ {
JFrame frame;
public static void main(String [] args) {
StackQ gui = new StackQ();
gui.go();
}
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("location test");
JButton button2 = new JButton("location test");
JButton button3 = new JButton("location test");
JButton button4 = new JButton("location test");
myDrawPanel custom = new myDrawPanel();
frame.getContentPane().add(button, BorderLayout.CENTER);
frame.getContentPane().add(button2, BorderLayout.EAST);
frame.getContentPane().add(button3, BorderLayout.WEST);
frame.getContentPane().add(button4, BorderLayout.SOUTH);
frame.getContentPane().add(custom, BorderLayout.NORTH);
frame.setSize(300,300);
frame.setVisible(true);
}
}
class myDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
Color random = new Color(red, green, blue);
g.setColor(random);
g.fillOval(20,20,100,100);
}
}
It could be clearer but this is described in the BorderLayout documentation:
The components are laid out according to their preferred sizes and the constraints of the container's size.... the CENTER component may stretch both horizontally and vertically to fill any space left over.
Put another way, the CENTER component will be stretched (if needed) to fill the application, and any other components need to specify a preferred-size in order to take some of that from the CENTER.
JButton specifies a preferred size by default, based on the contents of the button. JPanel on the other hand does not - its preferred size depends on its contents, and your JPanel doesn't have any contents, therefore its preferred size is zero.
In short, specify a preferred size for your JPanel and the BorderLayout will try to allocate at least that much space for the panel.
Just for any potential future viewers of this post, to get the desired result, I first imported the Dimension class at the very top of my StackQ class (this is needed because the setPreferredSize() method used later on in the go() method accepts an argument of type Dimension):
import java.awt.Dimension;
And then I added this code to the go() method immediately after the instantiation of the myDrawPanel class:
Dimension dims = new Dimension(1366, 200);
custom.setPreferredSize(dims);
I chose 1366 as the width because that's how big my screen is.
Thanks everyone for your help!

Create panel with horizontal wrap and vertical scroll

I have the following little program:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class ScrollPanePlay extends JFrame
{
public static void main(String[] args)
{
ScrollPanePlay frame = new ScrollPanePlay();
frame.setVisible(true);
}
public ScrollPanePlay()
{
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
JPanel panel = new JPanel(this);
panel.setLayout(new FlowLayout(FlowLayout.LEFT));
panel.add(new JLabel("one"));
panel.add(new JLabel("two"));
panel.add(new JLabel("three"));
panel.add(new JLabel("four"));
panel.add(new JLabel("five"));
JScrollPane scrollPane = new JScrollPane(panel);
add(panel, BorderLayout.CENTER); // <== add either panel or scrollpane
pack();
}
}
If I add panel, then the labels in the panel wrap when the window is made narrow, as expected for FlowLayout.
If I add the panel to scrollpane and add the scrollpane to the frame, then the labels don't wrap, because the scrollPane is reporting sufficient width (I guess) to hold all the labels, and allows the user to scroll to see them.
I would like the scrolling behavior, but only for vertical - I would like the user to choose the width he wants to see, and have the scrollbar appear only for scrolling vertically. How can I do that?
I tried extending JScrollPane and returning true from getScrollableTracksViewportWidth(), but, as I expected, that doesn't do what I want because it's the frame's width I want things to track, not the viewport. I tried extending JPanel and overriding getWidth to return the width of the frame, but that still left all the labels in a horizontal row, i.e., they quit wrapping. Is there something that will do this without a custom layout manager? Seems to me all we need is programmatic control of the viewport width.
It is a viewport issue. When inside a JScrollPane a JPanel wont resize to fit the JScrollPane size (that is kind of why ScrollPanes exist).
You can use a Scrollable Panel instead of a JPanel and set it to fit the maximum width (in this case it would fit to the JScrollPane).
Something like:
ScrollablePanel panel = new ScrollablePanel( new BorderLayout());
panel.setScrollableWidth( ScrollablePanel.ScrollableSizeHint.FIT );
Check this answer to a little more general answer and for a link to the .class.

Is this Swing behavior a bug? Mysterious BoxLayout alignment

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.

JScrollPane usage

I have a JScrollPane and JPanel inside it(I want to draw on JPanel). Also I have a method that draws lines with parametre of length of the line. I want my JScrollPane to scroll if the length of the line is more then the size(height or width) of my JPanel that is on JScrollPane.
How can I do this?
P.S. I've tried jScrollPane.scrollRectToVisible but it doesn't work
Here is an example implementing what you want
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class DrawLine {
JFrame j;
JPanel p;
JScrollPane sp;
public DrawLine() {
j = new JFrame();
p = new JPanel() {
public void paintComponent(Graphics g) {
g.drawLine(20, 20, 250, 250);
}
};
p.setPreferredSize(new Dimension(300, 300));
sp = new JScrollPane(p);
j.getContentPane().add(sp);
j.setSize(300, 300);
j.setVisible(true);
}
public static void main(String[] args) {
new DrawLine();
}
}
Note the line p.setPreferredSize(new Dimension(300, 300)); which sets the preferred size of the panel.
You need to define the following method in the LinePanel class:
public Dimension getPreferredSize() {
return new Dimension(myLine.getWidth(), myLine.getHeight());
}
Make sure that myLine object is available to the above method. And, you will get the scrollbars as needed.
P.S.: I assume LinePanel extends JPanel and is the panel on which the line is drawn.
You need to associate the JScrollPanel with the JPanel that it is scrolling. That is, you need
JScrollPane lineScrollPane = new JScrollPane(linePanel);
This will create a JScrollPane that automatically sizes itself to scroll over whatever linePanel's size is.
It's important to make sure then, that the panel containing your line actually IS big enough to fit entire line. That is, you might have something like
linePanel.setSize(myLine.getWidth(), myLine.getHeight());
or whatever your associated line size is. Note that something like
lineScrollPanel.setSize(new Dimension(600, 400));
will resize the scroll panel but not the thing inside--that is, it will be a 600x400 scroll pane but the inside panel may be bigger smaller, and it will be scrolled.

Categories