Adding JPanels to regions other than CENTER - java

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!

Related

How does one stretch a JButton to the full height of a JPanel

To summarize my problem, I am developing an interface for an application in Java. I have run into a problem I haven't previously faced yet.
To create a sort of "navigation bar" i created a thin JPanel at the top of the JFrame. Then I wanted to add a JButton to it to represent the "home" button which leads to the starting content of the application. When I did that and made width as well as height of the Button equal to the height of the Panel, the Button had a little gap of approximately 3 pixels to the top of the Panel. I don't know why that is, there is no other element inside the Panel. I am not using any layout, however i have tried to fix that problem using a FlowLayout but nothing really changed.
Here is the full code for the interface, i marked the menu panel with red color for testing purpose only. How do I get rid of that small gap to the top?
package UserInterface;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Interface {
private JFrame frame;
private JPanel menu;
private JPanel content;
public JButton home;
public Interface() {
Dimension dim = new Dimension(50, 50);
frame = new JFrame();
menu = new JPanel();
content = new JPanel();
frame.setSize(1600, 900);
menu.setSize(1600, 50);
content.setSize(1600, 850);
frame.setTitle("Tierland");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(
(int)((Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - (frame.getWidth() / 2)),
(int)((Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - (frame.getHeight() / 2))
);
frame.setSize(1600, 900);
menu.setSize(1600, 50);
content.setSize(1600, 850);
menu.setBackground(Color.RED);
home = new JButton();
home.setPreferredSize(dim);
home.setMinimumSize(dim);
home.setMaximumSize(dim);
home.setSize(dim);
frame.add(menu);
frame.add(content);
frame.setVisible(true);
menu.setVisible(true);
content.setVisible(true);
}
}
I am not using any layout,
Yes you are. By default a JPanel uses a FlowLayout.
however i have tried to fix that problem using a FlowLayout but nothing really changed.
That is because the default FlowLayout uses a 5 pixel gap around any component added to the panel.
You should be using:
menu.setLayout( new FlowLayout(...) );
Read the FlowLayout API for the appropriate constructor that will allow you to use 0, for the horizontal and/or vertical gap.
frame.add(menu);
frame.add(content);
Also, by default, the content pane of the frame uses a BorderLayout, so you should be using:
frame.add(menu, BorderLayout.PAGE_START);
frame.add(content, BorderLayout.CENTER);
You should not be attempting to control the size of the button:
//home.setPreferredSize(dim);
//home.setMinimumSize(dim);
//home.setMaximumSize(dim);
Just set the text of the button or add an Icon to the button and the button will determine its own preferred size.
Also, you really should call your "menu" panel a "toolbar" panel. Swing already supports menus and there is a special method to add a menu bar to the frame. Read the section from the Swing tutorial on How to Use Menus for more information.

How to center the view by middle panel in swing

I have to build a UI with three panels, leftPanel, middlePanel and rightPanel, Panels should be aligned Horizontally
left and right panels should get the maximum and equal width as possible while the middle panel can get the minimum width as its child component required, once the panels are added view should be centered by middle panel.
I have done following test code to build the UI
import java.awt.Color;
import java.awt.GridBagLayout;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class SwingTest extends JFrame {
private static final long serialVersionUID = 1L;
public SwingTest() {
setTitle("Swing Test");
setSize(750, 350);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(getParent());
// Rule used to find the component positions, for the testing purpose
Rule rule = new Rule(Rule.HORIZONTAL, true);
rule.setPreferredWidth(10);
rule.setPreferredHeight(40);
SidePanel leftPanel = new SidePanel("Left");
SidePanel rightPanel = new SidePanel("Right");
JPanel middlePanel = new JPanel();
middlePanel.setBorder(BorderFactory.createLineBorder(Color.black));
// Add left,right and middle panel horizontally
JPanel containerPanel = new JPanel();
containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.X_AXIS));
containerPanel.add(leftPanel);
containerPanel.add(middlePanel);
containerPanel.add(rightPanel);
// Add rule and container panel Vertically
JPanel outerPanel = new JPanel();
outerPanel.setLayout(new BoxLayout(outerPanel, BoxLayout.Y_AXIS));
outerPanel.add(rule);
outerPanel.add(containerPanel);
add(outerPanel);
}
public static void main(String[] args) {
SwingTest test = new SwingTest();
test.setVisible(true);
}
private static class SidePanel extends JPanel {
private static final long serialVersionUID = 1L;
private SidePanel(String text) {
setLayout(new GridBagLayout());
add(new JLabel(text));
}
}
}
I have used Rule class from oracle swing tutorial site, it can be used to find the position of components. get Rule.java
The problem i have is as shown in the UI screen shot, view is not get centered by middle panel, Right now the view centered as width of `left + middle panel = right panel'
I don't want to set the panel height and width manually, It should be handled by the LayoutManager itself.
What i want is middle panel has to be get centered in a way left + middle/2 = middle/2 + right panel width.
I couldn't find a way to make the middle panel to get centered to view, Could someone help me to find a solution for this?.
You need to understand how the BoxLayout works.
First space is allocated at the preferred size of each component. Then if there is extra space, that space is allocated to each component.
The middle doesn't appear to be centered because the preferred size of the right label is larger than the left label. So when the extra space is allocated the right side is larger than the left side.
This is easy to test. Just change the text of the right side panel to "LEFT".
However, that leads to another question - Why doesn't the size of the middle panel change? I don't know the answer to this. Although for some reason the default FlowLayout seems to work different than the GridBagLayout. Again you can test this by changing the layout of the middle panel to be a GridBagLayout.
If you always want the left/right panels to be the same size then you might want to consider using the Relative Layout. Using this layout the basic code would be:
RelativeLayout rl = new RelativeLayout(RelativeLayout.X_AXIS);
JPanel panel = new JPanel( rl );
panel.add(leftPanel, new Float(1));
panel.add(middle, new Float(3));
panel.add(rightPanel, new Float(1));
Now the left/right panels will be the same size independent of the components added to each panel.

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.

JPanel filled with Buttons inside a JScrollPane

I'm currently trying to fill a JPanel (using GridLayout) with Buttons. These Buttons should be squares having a set width.
The programm should be able to resize the playing field (n*m buttons). Problem is, the buttons are automatically stretched to completely fill the JScrollpane, completely ignoring the set height and width. Thus making the scrollbars unnecessary.
Here is my code:
Creating the panels:
panel_game = new javax.swing.JPanel();
jScrollPane1 = new javax.swing.JScrollPane(panel_game);
Trying to resize the JPanel: (where x and y are the number of buttons and zoomlvl the size)
panel_game.setLayout(new java.awt.GridLayout(x, y));
panel_game.setBounds(panel_game.getX()+5,panel_game.getY()+5,x*zoomlvl-5,y*zoomlvl);
panel_game.setPreferredSize(new Dimension(x*zoomlvl,y*zoomlvl));
And adding the buttons: (f is my playfield, a 2D-Array)(buttons is a Arraylist, containing my buttons)
for(int i=0; i<f.getSize()[0]*f.getSize()[1];i++){
buttons.add(new JButton());
buttons.get(i).setSize(zoomlvl, zoomlvl);
buttons.get(i).setPreferredSize(new Dimension(zoomlvl,zoomlvl));
panel_game.add(buttons.get(i));
}
I have no clue how I can tell Java to stop resizing my buttons etc. automatically.
Help would be appreciated :)
GridLayout is built to fill the container that uses it with components. If the container holding the buttons is constrained in size, then the buttons will stretch or fill to fill that container as closely as possible. A solution may be to nest layout managers so that this does not occur, but I can't give more specific advice without more code, particularly a minimal example program. Images might help too.
If you want to prevent the "button grid panel" from stretching to fill the whole viewport area of the ScrollPane, you could place this grid panel into another panel (with FlowLayout).
In general, you should either
not set the preferred size of the panel_game OR
not set the preferred size of the buttons
because
when you set the preferred size of the buttons, then the preferred size of the panel will be computed from the preferred sizes of the buttons
when you set the preferred size of the panel, then the size of the buttons will be determined by the size of the panel, respectively
Maybe this is what you want to achieve:
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class ScrollButtonGrid
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int x = 5;
int y = 5;
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(x, y));
for(int i=0; i<x*y;i++){
JButton button = new JButton(String.valueOf(i));
button.setPreferredSize(new Dimension(100,100));
panel.add(button);
}
JPanel container = new JPanel(new FlowLayout(FlowLayout.CENTER, 0,0));
container.add(panel);
JScrollPane scrollPane = new JScrollPane(container);
f.getContentPane().add(scrollPane);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}

Swing BoxLayout problem with JComboBox without using setXXXSize

here's an SSCCE:
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BoxLayoutTest extends JFrame {
public BoxLayoutTest(){
JPanel main = new JPanel();
main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS));
main.setBackground(Color.red);
this.add(main);
JPanel northPanel = new JPanel();
JPanel middle = new JPanel();
middle.setLayout(new BoxLayout(middle, BoxLayout.X_AXIS));
middle.add(new JButton("FOO"));
middle.add(Box.createHorizontalGlue());
JPanel aPanel = new JPanel();
aPanel.setBackground(Color.black);
JComboBox b = new JComboBox();
//b.setPreferredSize(new Dimension(100,16)); //uncomment this to see the layout I would like to achieve
//b.setMaximumSize(new Dimension(100,16));
//middle.add(b); //uncomment this line
middle.setBackground(Color.green);
northPanel.setBackground(Color.blue);
main.add(northPanel);
main.add(middle);
main.add(Box.createVerticalGlue());
this.setSize(800,600);
this.setResizable(true);
this.setVisible(true);
}
public static void main(String[] args) {
new BoxLayoutTest();
}
}
I'm trying to refactor some classes I wrote some time ago, when I didn't know that using setXXXSize methods on components is wrong.
Using a resizable frame ,the result I want to achieve is the following:
The northPanel should stay on top and change it's size accordingly to the frame size modifications (seems to work fine)
The green panel where I put the JButton should keep the maximum dimension of the JButton and stay just below the blue panel above (this works fine if I only put JButtons inside that panel).
The problem arise if I put a JComboBox inside the green panel (try to uncomment the line in the SSCCE). I guess JComboBox hasn't a maximum size specified, so it stretches with the frame. In the previous wrong version of my code I was using setxxxSize methods on the JComboBox to limit it's dimension(try to uncomment the line on setXXXSize methods to see it).
My question are:
Is it possible to achieve the same result using BoxLayout without invoking setXXXSize() methods?
If yes, how?
Is there any other LayoutManager that can I use to get that effect?
Please put me in the right direction
JComboBox is misbehaving (the same as JTextField) in reporting an unbounded max height: should never show more than a single line. Remedy is the same: subclass and return a reasonable height
JComboBox b = new JComboBox() {
/**
* #inherited <p>
*/
#Override
public Dimension getMaximumSize() {
Dimension max = super.getMaximumSize();
max.height = getPreferredSize().height;
return max;
}
};
just for fun, here's a snippet using MigLayout (which is my personal favorite currently :-)
// two panels as placeholders
JPanel northPanel = new JPanel();
northPanel.setBackground(Color.YELLOW);
JPanel southPanel = new JPanel();
southPanel.setBackground(Color.YELLOW);
// layout with two content columns
LC layoutContraints = new LC().wrapAfter(2)
.debug(1000);
AC columnContraints = new AC()
// first column pref, followed by greedy gap
.size("pref").gap("push")
// second
.size("pref");
// three rows, top/bottom growing, middle pref
AC rowContraints = new AC()
.grow().gap().size("pref").gap().grow();
MigLayout layout = new MigLayout(layoutContraints, columnContraints,
rowContraints);
JPanel main = new JPanel(layout);
main.setBackground(Color.WHITE);
// add top spanning columns and growing
main.add(northPanel, "spanx, grow");
main.add(new JButton("FOO"));
// well-behaved combo: max height == pref height
JComboBox combo = new JComboBox() {
#Override
public Dimension getMaximumSize() {
Dimension max = super.getMaximumSize();
max.height = getPreferredSize().height;
return max;
}
};
// set a prototype to keep it from constantly adjusting
combo.setPrototypeDisplayValue("somethingaslongasIwant");
main.add(combo);
// add top spanning columns and growing
main.add(southPanel, "spanx, grow");
I have always seen using the layout managers in the jdk are not easy. They are either too simple and inflexible or the gridbaglayout is just too much trouble. Instead I started using the jgoodies form layout and never looked back since.. Have a look at it. Its very simple and easy to use. Here's a link:
http://www.jgoodies.com/freeware/forms/
Make sure you go through the white paper.
And now, we also have google providing us a WYSISWG editor for the formlayout as a plugin for eclipse. This just makes life a lot lot easier.
http://code.google.com/javadevtools/wbpro/palettes/swing_palette.html

Categories