I already have a panel made (its a row of buttons), and have it located at the bottom of a frame (SOUTH), but I would like to add two rows (panels/ subpanels) beneath it (a text input line and output line if it matters). Right now the only thing I know to do is declare and add more panels, which would be fine, but when I specify .SOUTH they go over top of the previous panel.
EDIT: The solution I used
As suggested by Ted Hopp, I added my original panel (now called subPanel1), as well as the two new panels which were going on top of the original (subPanel2 & subPanel3), to a fourth "container panel" (bottomCotainerPanel). Since I only had three subPanels, this allowed me to specify where in the containerPanel each subPanel would go (using NORTH, CENTER, SOUTH, might have to do something slightly different if you had more than 3...), and then specify where the contianerPanel would go in the frame (SOUTH).
subPanel1.setLayout(new GridLayout(1,6)); //Layout of subPanel1
subPanel1.add(clearButton);
subPanel1.add(customerNameLabel);
subPanel1.add(customerNameTextField);
subPanel1.add(showByNameButton);
subPanel1.add(openNewSavingsButton);
subPanel1.add(openNewCheckingButton);
subPanel2.add(sendChatTextField);
subPanel2.add(sendButton);
subPanel2.add(clearButton2);
subPanel3.add(receiveChatTextField);
subPanel3.add(nextButton);
subPanel3.add(previousButton);
bottomContainerPanel.setLayout(new GridLayout(3,1)); //Layout of Container Panel
bottomContainerPanel.add(subPanel1, BorderLayout.NORTH);
bottomContainerPanel.add(subPanel2, BorderLayout.CENTER);
bottomContainerPanel.add(subPanel3, BorderLayout.SOUTH);
tellerWindow.getContentPane().add(bottomContainerPanel, BorderLayout.SOUTH);
You need to add a single container panel as the SOUTH panel of the frame. The container itself should have the layout that you want for everything that goes at the bottom.
If you just want to split panel onto 2 equal parts at south and north use GridLayout. If you want something in the middle you can use BorderLayout.
If you want to give your user ability to change the sub-panels size use JSplitPane.
I had a similar problem trying to put several rows of buttons into a panel borrowed from the ListDemo example. Well, the first thing to do is to read about BorderLayout: How to Use BorderLayout, or at least see the image shown there:
You cannot have multiple bottom rows in a BorderLayout. But you can use a nested layout. What we need is a BoxLayout, see How to Use BoxLayout:
.
We just have to replace the buttons shown on the above image by rows of buttons.
public class MyStuff extends JPanel {
...
public MyStuff() {
super(new BorderLayout());
...
JPanel buttonArea = new JPanel();
buttonArea.setLayout(new BoxLayout(buttonArea, BoxLayout.PAGE_AXIS));
add(buttonArea, BorderLayout.PAGE_END);
...
//if you dislike the default center alignment:
//panelWithButtons1.setAlignmentX(Component.LEFT_ALIGNMENT);
buttonArea.add(...);// add the 1st panel with buttons
buttonArea.add(...);// add the 2nd panel with buttons
buttonArea.add(...);// add the 3rd panel with buttons
Related
I'm trying to use a BoxLayout to display 2 panels vertically and I searched how to center the components in those panels. At the moment, my components are placed on the top center of each panel, and I want to get them at the center X and Y.
I added the components I want in the 2 panels, then I added the panels in my BoxLayout. This way they're displayed vertically as I want them to be, but as I said I don't want them to be on top center.
I tried to use methods such as setAlignementY and setLocation but any of them actually moves the components. I also saw that a BoxLayout will try to set the components as wide as the widest component, but as I have only 2 panels which have the same size I don't really understand it.
This is basically how I've added my components (without trying to center) :
private void initPanels ()
{
this.titlePanel.add(this.title);
this.bookInputPanel.add(bookTitle);
this.bookInputPanel.add(bookInput);
this.authorInputPanel.add(by);
this.authorInputPanel.add(authorInput);
this.authorInputPanel.add(this.authorsTable);
this.buttonsPanel.add(confirm);
this.contentPanel.setLayout(new BoxLayout(this.contentPanel, BoxLayout.Y_AXIS));
this.contentPanel.add(bookInputPanel);
this.contentPanel.add(authorInputPanel);
this.add(this.titlePanel, BorderLayout.NORTH);
this.add(this.contentPanel, BorderLayout.CENTER);
this.add(this.buttonsPanel, BorderLayout.SOUTH);
}
I made a picture to show you exactly what I want but it seems that I need 10 rep to do it, sorry about that.
This way they're displayed vertically as I want them to be, but as I said I don't want them to be on top center.
One way is to add "glue" to the top/bottom of the panel. This "glue" will expand to fill the extra space available to the panel:
this.contentPane.add(Box.createVerticalGlue());
this.contentPanel.add(bookInputPanel);
this.contentPanel.add(authorInputPanel);
this.contentPane.add(Box.createVerticalGlue());
Read the section from the Swing tutorial on How to Use BoxLayout for more information about the feature of a BoxLayout.
Another option might be to use a "wrapper" panel that uses a different layout manager. For example the GridBagLayout with the default constraints will automatically center the component horizontally/vertically:
//this.add(this.contentPanel, BorderLayout.CENTER);
JPanel wrapper = new JPanel( new GridBagLayout() );
wrapper.add( contentPanel );
this.add(wrapper, BorderLayout.CENTER);
I have a JPanel with a BoxLayout manager that contains subpanels. I want the components inside these subpanels to have a left alignment, but they always appear centered.
It looks like BoxLayout correctly aligns components that are inserted directly, but fails to do that when they are inside a subpanel.
I have modified the example found in http://www.java2s.com/Tutorial/Java/0240__Swing/YAxisAlignment.htm so each button is placed inside a subpanel, and then the subpanel is placed inside the main panel with the BoxLayout manager:
public class YAxisAlignX {
private static Container makeIt(String title, float alignment) {
String labels[] = { "--", "----", "--------", "------------" };
JPanel container = new JPanel();
container.setBorder(BorderFactory.createTitledBorder(title));
BoxLayout layout = new BoxLayout(container, BoxLayout.Y_AXIS);
container.setLayout(layout);
// modified loop. the original version does not create JPanel pan.
// adds the buttons directly the the JPanel container with the
// BoxLayout
for (int i = 0, n = labels.length; i < n; i++) {
JPanel pan = new JPanel();
JButton button = new JButton(labels[i]);
pan.add(button);
button.setAlignmentX(alignment);
pan.setAlignmentX(alignment);
container.add(pan);
}
return container;
}
public static void main(String args[]) {
JFrame frame = new JFrame("Alignment Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container panel1 = makeIt("Left", Component.LEFT_ALIGNMENT);
Container panel2 = makeIt("Center", Component.CENTER_ALIGNMENT);
Container panel3 = makeIt("Right", Component.RIGHT_ALIGNMENT);
Container contentPane = frame.getContentPane();
contentPane.setLayout(new FlowLayout());
contentPane.add(panel1);
contentPane.add(panel2);
contentPane.add(panel3);
frame.pack();
frame.setVisible(true);
}
}
If you execute this version, you can see that the buttons are all centered, despite the alignment set. Why is this happening? Is there any solution? In my case, every subpanel contains several components, and I do not want to add then directly to the main panel.
Thank you very much.
But they are aligned!
First of all, setAlignmentX changes a property at a JComponent level, the layout in which these components are placed may or may not use this information. For example, BoxLayout uses it but FlowLayout and BorderLayout don't.
In your case you are placing some panels with a vertical BoxLayout and you are aligning them in various ways, and it works! It just so happens that the the panels stretch themselves to fit the whole column so actually aligning doesn't change their appearance. You can see this by setting a Border around the panels:
pan.setBorder(BorderFactory.createLineBorder(Color.red));
See:
The fact that the panels contain a button or anything else is mostly irrelevant (it only influences the size that the panel wants to take, and not definitely), the BoxLayout is aligning panels not what is there inside the panels, that's the work of each panel's layout. That's why the buttons wouldn't have its alignment affected by the BoxLayout.
Now how are those buttons deciding its alignment? Well that's up to the layout they are in. The buttons are inside the pan panel that is using the default LayoutManager FlowLayout. Now, as I said, FlowLayout doesn't care for the alignmentX/Y property so the line:
button.setAlignmentX(alignment);
Doesn't accomplish anything. To align in a FlowLayout you need to change its alignment field through FlowLayout.setAligment(int) (docs), you can also do this in the constructor, so If we change the pan declaration to:
JPanel pan = new JPanel(new FlowLayout(FlowLayout.LEFT));
You'll also have the buttons aligned to the left inside their panels:
Of course, all the columns are aligned to the left since the parameter float alignment of makeIt does not influence the FlowLayout's alignment just the BoxLayout's one (and it doesn't matter). You might want to change that argument to an int and call the function with the different FlowLayout constants.
All in all, in the original version the line button.setAlignmentX(alignment); made sense because the buttons were added directly to the container panel and the BoxLayout aligned them properly:
However once you put the buttons inside other panels, the BoxLayout starts aligning the panels (which because of how panels works inside a BoxLayout they were being stretched to fill the whole width) not the buttons, and the buttons alignment is up to the panels layout. That's how it has to work so we can make consistent nested layouts.
I hope that's clear.
I am trying to build my own "Battleship" game and have problems with swing.
I now read endless docs on oracle tutorials on LayoutManagers, but not any of them works as I understand them. They only add a few buttons, but never two individual panels.
JPanel Background = new JPanel();
Background.setLayout(new BoxLayout(Background, BoxLayout.X_AXIS));
panelPlayer = new JPanel();
panelPlayer.setBorder(BorderFactory.createLineBorder(Color.black));
panelPlayer.setSize(700, 600);
// PC Field
panelPc = new JPanel();
panelPc.setBorder(BorderFactory.createLineBorder(Color.black));
panelPc.setSize(700, 600);
//adding to frame
getContentPane().add(Background);
Background.add(panelPlayer);
Background.add(panelPc);
After that I have a loop thats adds 16x16 buttons in a JButton[] once for every panel.
How to get the two panels to show a table layout?
I used GridLayout before, the grid works, but it always takes up the whole space of the frame, not of the Container or Panel or else. The panels are overlapping then.
GridBagLayout just puts the buttons in a row and beyond the screen.
Don't fix the size of the panel while using any layout. It works only when you use null layout
You can achieve your goal with GridBagLayout. While adding buttons specify gridx, gridy correctly, it will add buttons like table
just keep nesting the layouts.
in your case make a big one with two sides -
then in each side place another panel with your grid.
You can solve this by nesting panels. Each panel has its own layout manager, so it is a matter of breaking up your UI into pieces and choosing the layout manager for each piece.
If you want two panels side-by-side, then the panel that contains them should have a FlowLayout manager with horizontal orientation. Create a panel with FlowLayout and add the panels to it.
If each of the the side-by-side panels needs the grid of buttons, then set the panel layout to GridLayout and put the buttons in the panel. This fits what I remember of Battleship; in a grid layout, all the grid elements remain the same size no matter how the window is resized.
That should get you started. If, as I expect, you will want another panel with some game controls on it, look into BorderLayout; it has a section on each edge of a rectangle and another in the middle. Put the panel containing the two grids in the center of a panel using BorderLayout, and then your game controls can go in a panel to the north, south, east, or west of that.
Good luck. Let us know if you have a specific problem (in another question).
BorderLayout does something strange. If I add two panels to a Container with the same constraint (BorderLayout.CENTER for instance), then the first one goes away, even if the second one is deleted or made invisible
It seems as though it would make sense for it to "stack" each element on top of the previous ones.
Is this correct and by design? If so, is there some documentation on it?
Has anyone else been frustrated by it? Have you a solution, such as a custom LayoutManager?
Sample code:
JFrame frame = new JFrame();
frame.setSize(500, 500);
JPanel panel1 = new JPanel();
panel1.setBackground(Color.blue);
frame.getContentPane().add(panel1);
JPanel panel2 = new JPanel();
panel2.setBackground(Color.red);
frame.getContentPane().add(panel2);
panel2.setVisible(false); // Seems like it should allow us to see panel1.
frame.setVisible(true);
This creates and displays a 500x500 blank box.
BorderLayout was simply not designed to do what you want. Separation of responsibility. If you want that behavior you should compose: combine the BorderLayout with a CardLayout. Though for the actual stack behavior, you'll have to code something yourself (or find someone who already has.)
Is this correct and by design?
Yes.
You need to understand the basics of how layout managers work. One of the jobs of the layout manager is to set the "location" and "size" of the components added to the panel. In the case of a BorderLayout it only tracks 5 components so only the last component added to the CENTER is known by the layout manager.
Layout management is not done when components are added to the panel. It is done when the frame is packed, or made visible (or the revalidate() method is invoked) . In this case the blue panel is not part of the components managed by the BorderLayout so its size remains (0, 0), which means there is nothing to paint.
Try changing your code to:
JPanel panel1 = new JPanel();
panel1.setSize(200, 200);
and you will see the blue panel painted at the specified size.
Now try commenting out:
//panel2.setVisible(false);
and you will see both panels. This is because as components are added to the panel they are assigned a ZOrder. Basically the last component added is painted first, which is why the blue panel is painted on top of the red panel. Check out the setComponentZOrder() method of the Container class for more information.
The CardLayout is probably the layout manager you should be using, but you can check out the Overlap Layout as well.
This is what my program should look like and I'm a bit confused on where I should use different layouts.
I have a Window class which calls the Panel class and the Panel class calls the InputPanel and DisplayPanel classes. My InputPanel class calls my DetailsPanel, CrimePanel and ButtonPanel classes so they make up what is seen under the Input tab. I was told to use a BorderLayout for the whole window, and that the DetailsPanel (left panel) and CrimePanel should be GridLayout.
Does this mean I should:
Put the BorderLayout code in Panel and the GridLayout code in CrimePanel and DetailsPanel or
Put the BorderLayout code in Window and the GridLayout code in Panel?
alt text http://img137.imageshack.us/img137/6422/93381955.jpg
Okay, your description is a little bit confusing (or I'm still too tired today or didn't have enough caffeine yet). Your notion of "calling" panel classes from others is also a little weird.
But as far as I can see it, your first option is the correct one.
In general you just nest the objects at runtime, so it might look a little like the following:
InputPanel (has BorderLayout)
+--DetailsPanel (put in BorderLayout.WEST; has GridLayout)
| +--nameLabel
| +--nameTextField
| +--...
+--CrimePanel (put in BorderLayout.NORTH; has GridLayout)
| +--murderRadioButton
| +--arsonRadioButton
| +--...
+--ButtonPanel (put in BorderLayout.CENTER; has GridLayout)
+--button
You usually do this in the constructor of the appropriate class:
public class InputPanel {
public InputPanel() {
this.setLayout(new BorderLayout());
this.add(new DetailsPanel(), BorderLayout.WEST);
this.add(new CrimePanel(), BorderLayout.NORTH);
this.add(new ButtonPanel(), BorderLayout.CENTER);
}
}
public class DetailsPanel {
JLabel nameLabel;
JTextField nameField;
// ...
public DetailsPanel() {
this.setLayout(new GridLayout(5, 1));
nameLabel = new JLabel("Name");
nameField = new JTextField();
// ...
this.add(nameLabel);
this.add(nameField);
// ...
}
}
...
However, I see a small problem here: Since GridLayout doesn't allow components to span multiple columns you may need to nest other panels in the DetailsPanel on the left as well. You can get away with a single GridBagLayout which has the needed capabilities, or you nest other panels there:
DetailsPanel (has BorderLayout)
+--panel1 (has GridLayout with 2 rows, 1 column; put in BorderLayout.NORTH)
| +--nameLabel
| +--nameField
+--panel2 (has GridLayout with 3 rows, 2 columns; put in BorderLayout.CENTER)
+--dayField
+--dayLabel
+--monthField
+--...
From your description, option 1 should work. Give the Input JPanel a BorderLayout, with the Details, Crime and Button JPanels (at the west, north and south, respectively) having GridLayouts.
First of all you will need to put a JTabbedPane into the Window to contain your two tabs (input and display), each consisting of a JPanel.
The input panel could be subdivided using a BorderLayout as Joannes describes; another alternative is the GroupLayout introduced in Java 6, which is very powerful, but hard to wrap your mind around. It could be used to layout the entire tab in one panel.